题解 P1993 【小K的农场】

command_block

2018-12-29 15:31:41

Solution

复习了**差分约束**,顺手写个题解。 所谓差分约束问题,就是用图论解一些特殊不等式的操作。 假设有n个变量,形如$\Large{x_1,x_2,x_3...x_n}$ 满足一些不等式: $\Large{x_{a_1}-x_{b_1}>=c_1}$ $\Large{x_{a_2}-x_{b_2}>=c_2}$ $……$ $\Large{x_{a_3}-x_{b_3}>=c_3}$ 就是说某变量与某变量的差大于(等于)某某。 咋一看没什么头绪,不过某位天才一拍脑门,想到了图论中的**最短路**,并成功建模。 大家学单源最短路的时候,想必都处理过$dis$数组,其满足一个称为“三角不等式”的东西: 对于一条边来讲 $\large{dis[to]<=dis[from]+length}$ 想想最短路的“松弛”就理解了。 变形可得: $\large{dis[from]-dis[to]>=-length}$ 也就是说每条边其实对应着**dis数组中的一个不等式限制**。 这可以用来求解差分约束。 题目中的限制: 1.$\large{dis_a-dis_b>=c}$ 建边$a->b$权值为$-c$ 2.$\large{dis_a-dis_b<=c}$ 变形可得: $\large{dis_b-dis_a>=-c}$ 建边$b->a$权值为$c$ 3.$\large{dis_a==dis_b}$ 转化可得: $\large{dis_b-dis_a>=0}\ $ 与 $\ \large{dis_b-dis_a<=0}$ 建边$b->a$与$a->b$权值为$0$。 如何判断无解呢? 我们是按照最短路建的模,最短路无解就是对应**负环**。 反映到代数上就是变量无限小。 建出的图不一定连通,所以要做一番操作(超级汇点也行,多次spfa也行) 这里跟大家分享一个spfa判负环的小技巧,每次扫边集的时候记录工作次数,估摸着快要TLE了就认为有负环。 好像是称为**卡带**优化。 做题的时候很好用,但是比赛时慎用。 (~~比如说某场比赛考了这道原题,我卡带设小了,没了80分~~) Code: ```cpp #include<cstdio> #include<vector> #include<queue> using namespace std; int n,m,ord,aaa,from,to,len,s[10500]; vector<int> g[10500],l[10500]; queue<int> t; bool in[10500]; void addLine(int from,int to,int len) { g[from].push_back(to); l[from].push_back(len); } bool spfa() { for (int i=1;i<=n;i++) {t.push(i);in[i]=1;} while(!t.empty()){ if (aaa>5000000)return 1;//卡带 int u=t.front(); aaa+=g[u].size(); t.pop();in[u]=0; //printf("%d %d\n",u,t.size()); for (int i=0,v;i<g[u].size();i++) if (s[v=g[u][i]]>s[u]+l[u][i]){ s[v]=s[u]+l[u][i]; if (!in[v]){t.push(v);in[v]=1;} } }return 0; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++){ scanf("%d%d%d",&ord,&from,&to); //x-y>=-c change-into x->y(c) if (ord==1){ scanf("%d",&len); addLine(from,to,-len); //from-to>=len }if (ord==2){ scanf("%d",&len); addLine(from,to,len); //from-to<=len -> from-to>=-len }if (ord==3){ addLine(from,to,0); addLine(to,from,0); // from-to==0 -> from-to>=0 && from-to>=0 } } if (spfa())printf("No"); else printf("Yes"); return 0; } ```