最小费用最大流,一般解法如下:
在流量基础上,每条边还有权费用,即单位流量下的所需费用。在最大流量下,求最小费用。解法:在最大流算法基础上,每次按可行流增广改为每次用spfa按最小费用(用单位费用)增广,每次按每条边一单位费用求到达终点的最小费用(最短路),那么每次找到“最短路”(只是一条路,不是多条(dinic每次可以增广多条)),之后按这条路最大
可能流量增广(取这条路上残量最小的),直到无法增广为止。(实现细节点代码备注)。
该题题意:m个供应地向n个商店供应k种物品,对于每种物品,供应地有相应库存量,商店有相应需求量,从供应地j到商店i每单位物品需要路费cost,求满足所有商店的最小费用。
思路:对于每种商品,若供不应求,则无解。反之:对于每种物品,建立源点0,汇点 n+m+1,。从 0到供应地有边,流量为供应地库存,费用为0,从商店到汇点,流量为该商店需求量,费用为0,从供应地到商店,费用为路费,流量为inf,问题转化为最小费用最大流(满足所有商店,刚刚最大,满流)。
#include<iostream> #include<queue> #include<cstdio> using namespace std; int n,m,k; int shop[60][55];int sup[55][55]; //第i个shop/sup 的第j种商品的供求量 int fire[55][55][55]; //第i种商品从第k个供应商到第j个商店的路费. int e[8000][4];int head[105];int nume=0; const int inf=0x3f3f3f3f; void inline adde(int i,int j,int f,int cost) //addedge,注意反向边可以费用取反!因为“后悔”时候可以减去费用。 { e[nume][0]=j;e[nume][1]=head[i];head[i]=nume; e[nume][2]=f;e[nume++][3]=cost; e[nume][0]=i;e[nume][1]=head[j];head[j]=nume; e[nume][2]=0;e[nume++][3]=-cost; } void build(int ii) //建图 { for(int i=1+m;i<=n+m;i++) adde(i,n+m+1,shop[i-m][ii],0); for(int i=1;i<=m;i++) adde(0,i,sup[i][ii],0); for(int i=1;i<=m;i++) for(int j=m+1;j<=m+n;j++) adde(i,j,inf,fire[ii][j-m][i]); } int inq[105];int d[105]; //spfa bool spfa(int & sum) //每次求费用 { int pre[105]; int minf=inf; int pr[105]; for(int i=0;i<n+m+2;i++) { inq[i]=0; d[i]=inf; } pre[0]=-1 ; pr[0]=-1; //路径中,分别记录到点i的边,和i之前的点。(这题如果用矩阵建图要方便) queue<int>q; q.push(0);inq[0]=1;d[0]=0; while(!q.empty()) { int cur=q.front(); q.pop(); inq[cur]=0; for(int i=head[cur];i!=-1;i=e[i][1]) { int v=e[i][0]; if(e[i][2]>0&&e[i][3]+d[cur]<d[v]) { d[v]=e[i][3]+d[cur]; pr[v]=cur; //记录增广路 pre[v]=i; if(!inq[v]) { q.push(v); inq[v]=1; } } } } if(d[n+m+1]==inf)return 0; //无法增广 int cur=n+m+1; //目的点 while(cur!=0) //取路径上最小残流量边作为流量增广 { minf=e[pre[cur]][2]<minf?e[pre[cur]][2]:minf; cur=pr[cur]; } cur=n+m+1; while(cur!=0) //增广,改流量 { e[pre[cur]][2]-=minf; e[pre[cur]^1][2]+=minf; cur=pr[cur]; } sum+=d[n+m+1]*minf; //费用为单位费用(该路径下每条边单位流量之和)*流量 return 1; } int mincost() { int sum=0; while(spfa(sum)); //无法增广为止 return sum; } int sumshop[55];int sumsup[55]; int main() { while(scanf("%d%d%d",&n,&m,&k)&&(n||m||k)) { for(int i=1;i<=k;i++) { sumsup[i]=sumshop[i]=0; } for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) { scanf("%d",&shop[i][j]); sumshop[j]+=shop[i][j]; } for(int i=1;i<=m;i++) for(int j=1;j<=k;j++) { scanf("%d",&sup[i][j]); sumsup[j]+=sup[i][j]; } for(int i=1;i<=k;i++) for(int ii=1;ii<=n;ii++) for(int iii=1;iii<=m;iii++) scanf("%d",&fire[i][ii][iii]); int flag=1; for(int i=1;i<=k;i++) { if(sumsup[i]<sumshop[i]) //供不应求 { cout<<-1<<endl; flag=0; break; } } if(flag==0)continue; int sumcost=0; for(int i=1;i<=k;i++) //每种物品最小费用之和 { for(int ii=0;ii<n+m+5;ii++) head[ii]=-1; nume=0; build(i); sumcost+=mincost(); } cout<<sumcost<<endl; } }
最小费用最大流粗解 poj2516,布布扣,bubuko.com
原文地址:http://blog.csdn.net/u011498819/article/details/26077639