又是网络流的建模.二分枚举答案。做这道题时,感觉做过类似的,想都没想就用之前的那个思路做了,结果错的。之前的思路就是,先求出任意两点间的最短路径,然后通过当前枚举的限制,把两点间路径在这个限制内的两点直接i连一条双向边。建s,t,s点到所有点连一条有向边,所有点到t连一条有向边。但这思路是错的,因为虽然在建的图中,任何从s到t的一个流的路径中的相临两点是在限制中,但这些满足限制的边可能经过组合就会大于限制,那么通过这条路径的流肯定是不满足限制的,所以给出的限制在这种情况下就完全违背了限制的初衷,也就会得到错误的结果。任何一个牛要走的话,肯定是从一个点i运动到j(j可以为i),中间怎么走的不需要考虑。不管i,j中间的路径多复杂,肯定走的是最小花费路径,也就是求的最短路,因为每条边的容量和方向都是无限制的,所以流要从i到j,肯定一直可以走这条最短路。所以就可以抽象出一个新的二分图,左边是各点,右边也是各点,左边到右边的点如果两点间最短路径小于限制的话,就连一条容量无穷的有向边,因为它可以一直走。然后加s,t,s连向左边的点容量为每个点有的cow数。右边的所有点连向t,容量为点可容纳的cow数。个人感觉就是和匹配差不多,这样求最大流就是最终结果。刚开始还有一个错误的思路就是,认为一个点如果有牛,又有容量,就把牛直接放到这个点内,也就是直接确定了一些流的方案,给一些边赋了流值。但是虽然牛所在的点有容量,他还是可以移动的,而可能就是最最短花费时间的方案。
#include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int maxf=210*2; const int maxp=200000; const int inf=1<<20; const long long inf2=1152921504606000000; struct Edge { int from,to,flow,cap,next; }; Edge e[maxp]; int f,p,s,t,tot,head[maxf],maxflow,cur[maxf],d[maxf],amount,tn[maxf],tc[maxf]; long long tcost[maxf],path[maxf][maxf],high,low,mid,tem; void addedge(int from,int to,int cap) { e[tot].from=from;e[tot].to=to;e[tot].flow=0;e[tot].cap=cap; e[tot].next=head[from];head[from]=tot;tot++; e[tot].from=to;e[tot].to=from;e[tot].flow=0;e[tot].cap=0; e[tot].next=head[to];head[to]=tot;tot++; } void floyd() { int i,j,k; for(k=1;k<=f;k++) { for(i=1;i<=f;i++) { for(j=i+1;j<=f;j++) { path[i][j]=path[j][i]=min(path[i][j],path[i][k]+path[k][j]); } } } } void build(long long limit) { memset(head,-1,sizeof(head)); tot=0; int x,y,i,j; for(i=1;i<=f;i++) { x=i*2-1;y=i*2; addedge(s,x,tn[i]); addedge(y,t,tc[i]); } for(i=1;i<=f;i++) { for(j=1;j<=f;j++) { if(path[i][j]<=limit) addedge(i*2-1,j*2,inf); } } } bool bfs() { queue<int> q; memset(d,-1,sizeof(d)); d[s]=0; q.push(s); while(!q.empty()) { int x=q.front();q.pop(); for(int i=head[x];i!=-1;i=e[i].next) { if(d[e[i].to]==-1&&(e[i].cap-e[i].flow)>0) { tcost[e[i].to]=tcost[x]+path[x][e[i].to]; d[e[i].to]=d[x]+1; q.push(e[i].to); } } } if(d[t]==-1) return false; else return true; } int dfs(int x,int a) { if(x==t||a==0) return a; int tf,flow=0; for(int& i=cur[x];i!=-1;i=e[i].next) { if(d[x]+1==d[e[i].to]&&(tf=dfs(e[i].to,min(a,e[i].cap-e[i].flow)))>0) { e[i].flow+=tf; e[i^1].flow-=tf; flow+=tf; a-=tf; if(a==0) break; } } return flow; } bool dinic() { long long flow=0; while(bfs()) { memcpy(cur,head,(f*2+2)*sizeof(int)); flow+=dfs(s,inf); } if(flow<amount) return false; else return true; } long long solve() { low=0;high=inf2; while(high-low>0) { mid=low+(high-low)/2; build(mid); if(dinic()) high=mid; else low=mid+1; } return high; } int main() { //freopen("in.txt","r",stdin); while(cin>>f>>p) { tot=0;s=0;t=2*f+1;amount=0; int i,j; for(i=1;i<=f;i++) { for(j=1;j<=f;j++) { path[i][j]=(i==j)?0:inf2; } } for(i=1;i<=f;i++) { scanf("%d%d",&tn[i],&tc[i]); amount+=tn[i]; } int u,v,co; for(i=1;i<=p;i++) { scanf("%d%d%d",&u,&v,&co); if(path[u][v]>co) path[u][v]=path[v][u]=co; } tem=-1; floyd(); long long ou=solve(); printf("%I64d\n",ou==inf2?-1:ou); } return 0; }