转载请注明:http://blog.youkuaiyun.com/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;
}