转载请注明:http://blog.csdn.net/jiangshibiao/article/details/22852017
【原题】
1834: [ZJOI2010]network 网络扩容
Time Limit: 3 Sec Memory Limit: 64 MBSubmit: 1453 Solved: 721
[Submit][Status]
Description
给定一张有向图,每条边都有一个容量C和一个扩容费用W。这里扩容费用是指将容量扩大1所需的费用。求: 1、 在不扩容的情况下,1到N的最大流; 2、 将1到N的最大流增加K所需的最小扩容费用。
Input
输入文件的第一行包含三个整数N,M,K,表示有向图的点数、边数以及所需要增加的流量。 接下来的M行每行包含四个整数u,v,C,W,表示一条从u到v,容量为C,扩容费用为W的边。
Output
输出文件一行包含两个整数,分别表示问题1和问题2的答案。
Sample Input
5 8 2
1 2 5 8
2 5 9 9
5 1 6 2
5 1 1 8
1 2 8 7
2 5 4 9
1 2 1 1
1 4 2 1
1 2 5 8
2 5 9 9
5 1 6 2
5 1 1 8
1 2 8 7
2 5 4 9
1 2 1 1
1 4 2 1
Sample Output
13 19
30%的数据中,N<=100
100%的数据中,N<=1000,M<=5000,K<=10
30%的数据中,N<=100
100%的数据中,N<=1000,M<=5000,K<=10
HINT
Source
【序言】啦啦啦,第一次做费用流~~先介绍一下。
(最小)费用流的概念:每条边新增一个权——单位费用。在满足最大流的情况下,使总费用最小。
算法:每次用SPFA找到一条花费最短的路。
【分析】第一问就直接dinic吧,正好也熟练一下。
第二问真的是纠结了很长时间(刚做,没什么经验啊)。原来以为应该重新建边,两两可到达的点容量改成K,而费用就是给定的。但是这样会有问题:有可能在残量网络中某些边仍然有流量,这样就不用费用了呀!于是我们可以把第一问做完后的残量网络拿出来,设定每条边的费用是0.然后像刚才说的那样建立一些有费用的边。
【注意】经WCY大牛的指导,我发现一个问题:在做费用流的时候求出的解是满足最大流情况下的解。设第一问的最大流是ANS,要扩容K,有可能在加了新边后最大流大于ANS+K。这样费用可能不是最小的。因此我们可以在1号或N号点新增一条边,容量是K,费用是0。这样就限制了最大流。
【代码】
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int M=5005; const int N=1005; const int INF=2139062143; struct arr2{int go,s,w,next;}a[M*4]; int f[N],q[N*5],end[N],pre[N]; int ans,flow,n,m,k,cnt,x[M],y[M],c,w,i,money[M]; bool flag[N]; void add(int u,int v,int c,int w) { a[++cnt].go=v;a[cnt].next=end[u];a[cnt].s=c;end[u]=cnt;a[cnt].w=w; } bool bfs() { memset(f,-1,sizeof(f)); memset(q,0,sizeof(q)); int h=0,t=1;q[1]=1;f[1]=1; while (h<t) { int now=q[++h];if (now==n) return 1; for (int i=end[now];i>=0;i=a[i].next) { int go=a[i].go; if (f[go]==-1&&a[i].s) { f[go]=f[now]+1; q[++t]=go; } } } return 0; } int dinic(int sta,int sum) { if (sta==n) return sum; int os=sum; for (int i=end[sta];i>=0&&os;i=a[i].next) { int go=a[i].go; if (f[go]==f[sta]+1&&a[i].s) { int Min=dinic(go,min(os,a[i].s)); a[i].s-=Min;a[i^1].s+=Min;os-=Min; } } if (os==sum) f[sta]=-1;return sum-os; } bool spfa() { int h=0,t=1; memset(f,0X7f,sizeof(f)); memset(q,0,sizeof(q)); memset(flag,0,sizeof(flag)); f[1]=0;q[1]=1;flag[1]=true; while (h<t) { int now=q[++h]; for (int i=end[now];i>=0;i=a[i].next) { int go=a[i].go; if (a[i].s&&f[now]+a[i].w<f[go]) { f[go]=f[now]+a[i].w;pre[go]=i; if (!flag[go]) flag[go]=true,q[++t]=go; } } flag[now]=false; } if (f[n]==INF) return 0;return 1; } void cost() { int sum=INF; for (int i=pre[n];i>=0;i=pre[a[i^1].go]) { sum=min(sum,a[i].s); if (a[i^1].go==1) break; } for (int i=pre[n];i>=0;i=pre[a[i^1].go]) { a[i].s-=sum; a[i^1].s+=sum; ans+=sum*a[i].w; if (a[i^1].go==1) break; } } int main() { memset(f,0X7f,sizeof(f)); scanf("%d%d%d",&n,&m,&k); for (i=0;i<=n;i++) end[i]= cnt=-1; for (i=1;i<=m;i++) { scanf("%d%d%d%d",&x[i],&y[i],&c,&w); money[i]=w; add(x[i],y[i],c,0);add(y[i],x[i],0,0); } flow=0; while (bfs()) flow+=dinic(1,INF); printf("%d ",flow); for (i=1;i<=m;i++) { add(x[i],y[i],INF,money[i]); add(y[i],x[i],0,-money[i]); } add(n,n+1,k,0);add(n+1,n,0,0);n++; while (spfa()) cost(); printf("%d",ans); return 0; }