http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3496
题目大意:A公司从S到T运货,每条路都有一个运货上限,而B公司则有p点费用点可以分配到个个路上,设分配到一条路上的费用点为x,该路走了y个货物,则需收费x*y,在以运货最大为要求下,B公司想要最大化收费,A公司则需要求出费用最大时的最小值(最小化费用)。
但是还没完,你还需要求出同条件下B公司想要最小化收费,A公司费用最小时的最大值(最大化费用)。
————————————————————————————
一道好题,顺便把我前面博客的bug找了出来。
首先需要想到,做到费用最大时,我们大可以把p点都加在流量最大的边上,这样一定是费用最大的(贪心策略)。
并且看到最大值最小,我们立刻想到了二分答案。
那么我们就可以二分所放p点的流量求解。
那么我们将所有边的容量都更新为min(容量,二分答案)重新跑一遍最大流,并且和之前的最大流相比,如果变小了的话就说明答案一定是小的,需要变大,否则答案就过大,需要变小。
此时我们已经做完了第一问,现在思考第二问。
我们用同样的思路二分,将所有路的容量增加一个我们二分答案为下界跑上下界网络流最大流,与原最大流比较即可,更新的方式和上面正好相反。
PS:一组数据如下:
1
5 11 2 1 2
3 4 12
4 1 11
0 1 8
2 3 11
4 0 12
3 1 9
2 4 11
4 3 8
0 3 6
2 0 8
2 1 11
ans:22 12
这组数据特别之处在于它有一条从u到v的边,同时还有从v到u的边,一旦有这种情况,只要在上下界网络流中处理不好就会出错。
显然我们只能删除超级源汇点(甚至你可以不删除,也不会对答案有什么影响),而不能将其他的边删除,否则就会造成残余网络中的流量无法留到汇点从而WA掉。
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N=605; const int M=20005; const int INF=0x3f3f3f3f; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch=='-';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct node{ int nxt,to,w; }edge[M<<1]; struct line{ int u,v,w; }e[M]; int head[N],du[N],cnt=-1,ans,maxw; int S,T,n,m,st,ed; ll p; inline void add(int u,int v,int w){ cnt++; edge[cnt].to=v; edge[cnt].w=w; edge[cnt].nxt=head[u]; head[u]=cnt; return; } int lev[N],cur[N],dui[N]; bool bfs(int k){ int r=0; for(int i=1;i<=k;i++){ lev[i]=-1; cur[i]=head[i]; } dui[0]=S,lev[S]=0; int u,v; for(int l=0;l<=r;l++){ u=dui[l]; for(int e=head[u];e!=-1;e=edge[e].nxt){ v=edge[e].to; if(edge[e].w>0&&lev[v]==-1){ lev[v]=lev[u]+1; r++; dui[r]=v; if(v==T)return 1; } } } return 0; } int dinic(int u,int flow,int k){ if(u==k)return flow; int res=0,delta; for(int &e=cur[u];e!=-1;e=edge[e].nxt){ int v=edge[e].to; if(edge[e].w>0&&lev[u]<lev[v]){ delta=dinic(v,min(edge[e].w,flow-res),k); if(delta>0){ edge[e].w-=delta; edge[e^1].w+=delta; res+=delta; if(res==flow)break; } } } if(res!=flow)lev[u]=-1; return res; } ////////// bool check1(int k){ memset(head,-1,sizeof(head)); cnt=-1; for(int i=1;i<=m;i++){ add(e[i].u,e[i].v,min(k,e[i].w)); add(e[i].v,e[i].u,0); } int awe=0;S=st,T=ed; while(bfs(n))awe+=dinic(S,INF,T); return awe==ans; } ll solve1(){ int l=0,r=maxw; ll awe=0; while(l<=r){ int mid=(l+r)>>1; if(check1(mid)){ awe=mid; r=mid-1; }else l=mid+1; } return awe*p; } ////////////// bool check2(int k){ memset(head,-1,sizeof(head)); memset(du,0,sizeof(du)); cnt=-1; for(int i=1;i<=m;i++){ if(e[i].w<k)return 0; add(e[i].u,e[i].v,e[i].w-k); add(e[i].v,e[i].u,0); du[e[i].u]-=k; du[e[i].v]+=k; } add(ed,st,INF);add(st,ed,0); S=n+1;T=S+1; int awe=0,full=0; for(int i=1;i<=n;i++){ if(du[i]>0){ add(S,i,du[i]); add(i,S,0); full+=du[i]; } else if(du[i]<0){ add(i,T,-du[i]); add(T,i,0); } } while(bfs(T))awe+=dinic(S,INF,T); if(full!=awe)return 0; head[S]=head[T]=-1; S=st;T=ed; awe=0; while(bfs(n))awe+=dinic(S,INF,T); return awe==ans; } ll solve2(){ int l=0,r=maxw; ll awe=0; while(l<=r){ int mid=(l+r)>>1; if(check2(mid)){ awe=mid; l=mid+1; }else r=mid-1; } return awe*p; } int main(){ int t=read(); while(t--){ n=read(),m=read(),st=read()+1,ed=read()+1,p=read(); memset(head,-1,sizeof(head)); maxw=0;cnt=-1; for(int i=1;i<=m;i++){ e[i].u=read()+1,e[i].v=read()+1,e[i].w=read(); add(e[i].u,e[i].v,e[i].w); add(e[i].v,e[i].u,0); maxw=max(maxw,e[i].w); } S=st,T=ed;ans=0; while(bfs(n))ans+=dinic(S,INF,T); printf("%lld %lld ",solve1(),solve2()); } return 0; }