题目描述
如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用。
输入输出格式
输入格式:
第一行包含四个正整数N、M、S、T,分别表示点的个数、有向边的个数、源点序号、汇点序号。
接下来M行每行包含四个正整数ui、vi、wi、fi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi),单位流量的费用为fi。
输出格式:
一行,包含两个整数,依次为最大流量和在最大流量情况下的最小费用。
输入输出样例
输入样例#1:
4 5 4 3 4 2 30 2 4 3 20 3 2 3 20 1 2 1 30 9 1 3 40 5
输出样例#1:
50 280
说明
时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=10,M<=10
对于70%的数据:N<=1000,M<=1000
对于100%的数据:N<=5000,M<=50000
样例说明:
如图,最优方案如下:
第一条流为4-->3,流量为20,费用为3*20=60。
第二条流为4-->2-->3,流量为20,费用为(2+1)*20=60。
第三条流为4-->2-->1-->3,流量为10,费用为(2+9+5)*10=160。
故最大流量为50,在此状况下最小费用为60+60+160=280。
故输出50 280。
思路:费用流
因为要注意费用的问题,所以用SPFA找费用最小的增广路,不要用Dijkstra(因为有负边权)(然而一般情况下SPFA更好写,所以这个警告是废话);
然后增广ap()。
几个注意的地方:
反边费用为其对应边的相反数;
队列首尾指针记得清零;(RE了50%左右)
有一种常(la)数(ji)级优化技巧叫先留个坑,反边随用随建。(优化程度达不到O()*0.5,但是够用,而且很好实现);
代码实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #define maxn 5010 4 #define maxm 100010 5 #define maxt 2139062143 6 int n,m,s,t,nflow,nfee,flow,fee; 7 int a,b,c,d; 8 long long la,lb; 9 int h[maxn],hs=1; 10 struct edge{int s,n,w,f;}e[maxm]; 11 int w[maxn]; 12 int p[maxn][2]; 13 int q[maxm],head,tail; 14 int min(int x,int y){return x<y?x:y;} 15 int spfa(){ 16 memset(w,0x7f,sizeof(w)); 17 head=tail=0; 18 q[head++]=s,w[s]=0; 19 while(head>tail){ 20 a=q[tail++]; 21 for(int i=h[a];i;i=e[i].n) 22 if(e[i].w){ 23 la=e[i].f,lb=w[a],la+=lb,lb=w[e[i].s]; 24 if(la<lb){ 25 w[e[i].s]=la; 26 p[e[i].s][0]=i; 27 p[e[i].s][1]=a; 28 q[head++]=e[i].s; 29 } 30 } 31 } 32 return w[t]; 33 } 34 int ap(int k,int v){ 35 if(k==s) return v; 36 int ret=ap(p[k][1],min(e[p[k][0]].w,v)); 37 if(!e[p[k][0]^1].f) e[p[k][0]^1]=(edge){p[k][1],h[k],0,-e[p[k][0]].f},h[k]=p[k][0]^1; 38 e[p[k][0]].w-=ret; 39 e[p[k][0]^1].w+=ret; 40 return ret; 41 } 42 bool Mcmf(){ 43 nfee=spfa(); 44 if(nfee==maxt) return false; 45 nflow=ap(t,maxt); 46 flow+=nflow; 47 fee+=nflow*nfee; 48 return true; 49 } 50 int main(){ 51 scanf("%d%d%d%d",&n,&m,&s,&t); 52 for(int i=1;i<=m;i++){ 53 scanf("%d%d%d%d",&a,&b,&c,&d); 54 e[++hs]=(edge){b,h[a],c,d},h[a]=hs++; 55 } 56 while(Mcmf()); 57 printf("%d %d ",flow,fee); 58 return 0; 59 }
代码重构:
struct换为数组;(更快)
函数重构;(更优美)
结果:916ms
1 #include<cstdio> 2 #include<cstring> 3 const int maxn=1e4+10; 4 const int maxm=1e5+10; 5 const int maxt=2139062143; 6 inline int min_(int x,int y){return x<y?x:y;} 7 int n,m,s,t,nflow,nfee,flow,fee; 8 int a,b,c,d; 9 int h[maxn],hs=1; 10 int e_q[maxm],e_z[maxm],e_n[maxm],e_w[maxm],e_f[maxm]; 11 int add(int q,int z,int k,int w,int f){e_q[k]=q,e_z[k]=z,e_n[k]=h[q],e_w[k]=w,e_f[k]=f,h[q]=k;} 12 int q[maxm],head,tail; 13 int w[maxn],p[maxn]; 14 bool v[maxn]; 15 int ap(int k,int v){ 16 if(k==s) return v; 17 int ret=ap(e_q[p[k]],min_(v,e_w[p[k]])); 18 if(!e_f[p[k]^1]) add(k,e_q[p[k]],p[k]^1,0,-e_f[p[k]]); 19 e_w[p[k]]-=ret,e_w[p[k]^1]+=ret; 20 return ret; 21 } 22 void Mcmf(){ 23 while(1){ 24 memset(w,0x7f,sizeof(w)); 25 memset(v,0,sizeof(v)); 26 head=tail=w[s]=0; 27 q[head++]=s,v[s]=1; 28 while(head>tail){ 29 a=q[tail++],v[a]=0; 30 for(int i=h[a];i;i=e_n[i]) 31 if(0ll+e_f[i]+w[a]<w[e_z[i]]&&e_w[i]){ 32 p[e_z[i]]=i; 33 w[e_z[i]]=e_f[i]+w[a]; 34 if(!v[e_z[i]]) q[head++]=e_z[i],v[e_z[i]]=1; 35 } 36 } 37 if(w[t]==maxt) break; 38 nflow=ap(t,maxt); 39 flow+=nflow; 40 fee+=nflow*w[t]; 41 } 42 } 43 int main(){ 44 scanf("%d%d%d%d",&n,&m,&s,&t); 45 for(int i=1;i<=m;i++){ 46 scanf("%d%d%d%d",&a,&b,&c,&d); 47 ++hs,add(a,b,hs,c,d),hs++; 48 } 49 Mcmf(); 50 printf("%d %d ",flow,fee); 51 return 0; 52 }
题目来源:洛谷