题目大意:一张n个节点的有向带边权图,每次操作能任选一个节点v个一个整数d,使以v为终点的边权值都减少d,以v为起点的边权值都增加d,求若干次操作后的最小边权值的非负最大值。
题目分析:用sum[i]表示作用在节点i上的所有d之和,则对于边a->b,操作若干次后的权值为w(a,b)+sum[a]-sum[b],假设最小边权值的最大值为x,则有sum[b]-sum[a]≤w(a,b)-x。这样的所有m条边构成一个差分约束系统。二分枚举x,求解即可。
代码如下:
# include<iostream> # include<cstdio> # include<queue> # include<cstring> # include<algorithm> using namespace std; const int INF=1<<30; struct Edge { int to,nxt,w; }; Edge e[3500]; int cnt,n,head[3000],inq[505],vis[505],dist[505]; void add(int u,int v,int w) { e[cnt].to=v; e[cnt].w=w; e[cnt].nxt=head[u]; head[u]=cnt++; } bool bellman_ford(int M) { queue<int>q; for(int i=1;i<=n;++i){ vis[i]=inq[i]=1; dist[i]=0; q.push(i); } while(!q.empty()) { int u=q.front(); q.pop(); inq[u]=0; for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].to; if(dist[v]>dist[u]+e[i].w-M){ dist[v]=dist[u]+e[i].w-M; if(!inq[v]){ inq[v]=1; q.push(v); if(++vis[v]>n-1) return true; } } } } return false; } int main() { int m,a,b,c; while(~scanf("%d%d",&n,&m)) { cnt=0; memset(head,-1,sizeof(head)); int maxn=0; while(m--) { scanf("%d%d%d",&a,&b,&c); add(a,b,c); maxn=max(maxn,c); } if(bellman_ford(1)) printf("No Solution "); else if(!bellman_ford(maxn+1)) printf("Infinite "); else{ int l=1,r=maxn; while(l<r){ int x=l+(r-l+1)/2; if(bellman_ford(x)) r=x-1; else l=x; } printf("%d ",l); } } return 0; }