【UVA11478】Halum (最短路解差分约束)
题目:
Sample Input
2 1
1 2 10
2 1
1 2 -10
3 3
1 2 4
2 3 2
3 1 5
4 5
2 3 4
4 2 5
3 4 2
3 1 0
1 2 -1
Sample Output
Infinite
Infinite
3
1
题意:
给定一个有向图,每条边都有一个权值。每次你可以选择一个结点v和一个整数d,把所有以v为终点的边的权值减小d,把所有以v为起点的边的权值增加d,最后让所有边的权值的最小值大于零且尽量大。
分析:
因为不同的操作互不影响,因此可以按任意顺序实施这些操作。另外,对于同一个点的多次操作可以合并,因此可以令sum(u)为作用于结点u之上的所有d之和。这样,本题的目标就是确定所有的sum(u),使得操作之后所有边权的最小值尽量大。
“最小值最大”又让我们想到使用二分答案的方法。二分答案x之后,问题转化为是否可以让操作完毕后每条边的权值均不小于x。对于边a->b,不难发现操作完毕后它的权值为w(a,b)+sum(a)-sum(b),因此每条边a->b都可以列出一个不等式w(a,b)+sum(a)-sum(b)>=x,移项得sum(b)-sum(a)<=w(a,b)-x。这样,我们实际得到一个差分约束系统。
差分约束系统是指一个不等式组,每个不等式形如xj-xi<=bk,这里的bk是一些事先已知的常数。这个不等式类似于最短路中的不等式d[v]<=d[u]+w(u,v),我们可以用最短路算法求解:对于约束条件xj-xi<=bk,新建一条边i->j,(根据最短路性质可以证明在图无负环的情况下这个不等式是成立的)权值为bk。如果图中有负权环,则差分约束系统无解。
代码如下:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 using namespace std; 8 #define Maxn 510 9 #define Maxm 4010 10 #define INF 0xfffffff 11 12 int n,m; 13 int first[Maxn],dis[Maxn],cnt[Maxn]; 14 bool bq[Maxn],inq[Maxn]; 15 16 struct node 17 { 18 int x,y,c,cc,next; 19 }t[Maxm];int len; 20 21 int mymax(int x,int y) {return x>y?x:y;} 22 23 void ins(int x,int y,int cc) 24 { 25 t[++len].x=x;t[len].y=y;t[len].cc=cc; 26 t[len].next=first[x];first[x]=len; 27 } 28 29 queue<int > q; 30 31 bool spfa(int s) 32 { 33 memset(inq,0,sizeof(inq)); 34 memset(dis,63,sizeof(dis)); 35 memset(cnt,0,sizeof(cnt)); 36 while(!q.empty()) q.pop(); 37 dis[s]=0;inq[s]=1;q.push(s); 38 while(!q.empty()) 39 { 40 int x=q.front();q.pop();inq[x]=0; 41 for(int i=first[x];i;i=t[i].next) 42 { 43 int y=t[i].y; 44 if(dis[y]>dis[x]+t[i].c) 45 { 46 dis[y]=dis[x]+t[i].c; 47 if(!inq[y]) 48 { 49 q.push(y); 50 inq[y]=1; 51 if(++cnt[y]>n+1) return 1; 52 } 53 } 54 } 55 } 56 return 0; 57 } 58 59 bool check(int x) 60 { 61 memset(bq,0,sizeof(bq)); 62 for(int i=1;i<=len-n;i++) t[i].c=t[i].cc-x; 63 if(spfa(n+1)) return 0; 64 /*for(int i=1;i<=n+1;i++) if(!bq[i]) 65 { 66 if(spfa(i)) return 0; 67 }*/ 68 return 1; 69 } 70 71 void ffind(int l,int r) 72 { 73 while(l<r) 74 { 75 int mid=(l+r+1)>>1; 76 if(check(mid)) l=mid; 77 else r=mid-1; 78 } 79 printf("%d\n",l); 80 } 81 82 int main() 83 { 84 while(scanf("%d%d",&n,&m)!=EOF) 85 { 86 memset(first,0,sizeof(first)); 87 int mx=-INF;len=0; 88 for(int i=1;i<=m;i++) 89 { 90 int x,y,cc; 91 scanf("%d%d%d",&x,&y,&cc); 92 ins(x,y,cc); 93 mx=mymax(cc,mx); 94 } 95 for(int i=1;i<=n;i++) 96 { 97 ins(n+1,i,0);t[len].c=0; 98 } 99 if(check(mx+1)) {printf("Infinite\n");continue;} 100 if(!check(1)) {printf("No Solution\n");continue;} 101 ffind(1,mx); 102 } 103 return 0; 104 }
2016-04-10 15:33:20