刘汝佳新书--训练指南
题意:给定一个有向图,每条边都有一个权值。每次你可以选择一个结点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。如果图中有负权环,则差分约束系统无解。
// File Name: 11478.cpp // Author: zlbing // Created Time: 2013/2/15 13:16:21 #include<iostream> #include<string> #include<algorithm> #include<cstdlib> #include<cstdio> #include<set> #include<map> #include<vector> #include<cstring> #include<stack> #include<cmath> #include<queue> using namespace std; #define MAXN 505 #define INF 10050 struct Edge{ int from,to; int dist; }; struct BellmanFord{ int n,m; vector<Edge>edges; vector<int>G[MAXN]; bool inq[MAXN]; int d[MAXN]; int p[MAXN]; int cnt[MAXN]; void init(int n) { this->n=n; for(int i=0;i<n;i++)G[i].clear(); } void AddEdge(int from,int to,int dist) { edges.push_back((Edge){from,to,dist}); m=edges.size(); G[from].push_back(m-1); } bool negativeCycle() { queue<int>Q; memset(inq,0,sizeof(inq)); memset(cnt,0,sizeof(cnt)); for(int i=0;i<n;i++) { d[i]=0;inq[0]=true;Q.push(i); } while(!Q.empty()) { int u=Q.front();Q.pop(); inq[u]=false; for(int i=0;i<G[u].size();i++) { Edge& e=edges[G[u][i]]; if(d[e.to]>d[u]+e.dist) { d[e.to]=d[u]+e.dist; p[e.to]=G[u][i]; if(!inq[e.to]) { Q.push(e.to); inq[e.to]=true; if(++cnt[e.to]>n) return true; } } } } return false; } }; BellmanFord solver; bool test(int x) { for(int i=0;i<solver.m;i++) solver.edges[i].dist-=x; bool ret=solver.negativeCycle(); for(int i=0;i<solver.m;i++) solver.edges[i].dist+=x; return ret; } int main(){ int n,m; while(~scanf("%d%d",&n,&m)) { int a,b,c; solver.init(n); for(int i=0;i<m;i++) { scanf("%d%d%d",&a,&b,&c); a--,b--; solver.AddEdge(a,b,c); } if(!test(INF))printf("Infinite\n"); else if(test(1))printf("No Solution\n"); else{ int L=1,R=INF; while(L<R) { int mid=L+(R-L+1)/2; if(test(mid))R=mid-1; else L=mid; } printf("%d\n",L); } } return 0; }