1、K种物品,M个供应商,N个收购商。每种物品从一个供应商运送到一个收购商有一个单位运费。每个收购商都需要K种物品中的若干。求满足所有收购商需求的前提下的最小运费。
2、K种物品拆开来,分别对每种物品进行最小费用最大流计算。
建立超级源点和超级汇点:超级源点流向M个供应商,容量为供应商的存储量,费用为0;N个收购商流向超级源点,容量为收购商的需求量,费用为0。
另外,供应商流向收购商,容量为无穷大,费用为对应的单位运费。
3、
1、Bellman-Ford:
#include<iostream> #include<stdio.h> #include<vector> #include<string.h> #include<queue> using namespace std; const int maxn=1024; const int INF=0x3f3f3f3f; struct Edge{ int from,to,cap,flow,cost; Edge(int u,int v,int c,int f,int w):from(u),to(v),cap(c),flow(f),cost(w){} }; struct MCMF{ int n,m; vector<Edge>edges; vector<int>G[maxn]; int inq[maxn];//是否在队列中 int d[maxn];//Bellman-Ford int p[maxn];//上一条弧 int a[maxn];//可改进量 void init(int n){ this->n=n; for(int i=0;i<n;i++)G[i].clear(); edges.clear(); } void AddEdge(int from,int to,int cap,int cost){ edges.push_back(Edge(from,to,cap,0,cost)); edges.push_back(Edge(to,from,0,0,-cost)); m=edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BellmanFord(int s,int t,int &flow,long long &cost){ for(int i=0;i<n;i++)d[i]=INF; memset(inq,0,sizeof(inq)); d[s]=0;inq[s]=1;p[s]=0;a[s]=INF; queue<int>Q; Q.push(s); while(!Q.empty()){ int u=Q.front();Q.pop(); inq[u]=0; for(int i=0;i<(int)G[u].size();i++){ Edge &e=edges[G[u][i]]; if(e.cap>e.flow&&d[e.to]>d[u]+e.cost){ d[e.to]=d[u]+e.cost; p[e.to]=G[u][i]; a[e.to]=min(a[u],e.cap-e.flow); if(!inq[e.to]){Q.push(e.to);inq[e.to]=1;} } } } if(d[t]==INF)return false; flow+=a[t]; cost+=(long long)d[t]*(long long)a[t]; for(int u=t;u!=s;u=edges[p[u]].from){ edges[p[u]].flow+=a[t]; edges[p[u]^1].flow-=a[t]; } return true; } //需要保证初始网络中没有负权圈 int MincostMaxflow(int s,int t,long long &cost){ int flow=0;cost=0; while(BellmanFord(s,t,flow,cost)); return flow; } }MM; int main(){ int N,M,K;//汇点个数,源点个数,物品种类 int need[64][64];//need[i][j]表示第i个店主需要第j种商品的数量 int totalneed[64];//totalneed[i]表示第i种商品的总需求量 int storage[64][64];//storage[i][j]表示第i个仓库里存储的第j种商品的数量 int totalstorage[64];//totalstorage[i]表示第i种商品的总存储量 int cost[64][64];//cost[i][j]表示当前这个商品从第j个仓库运送到第i个店主的单位价格 bool flag;//false表示存储量不足 long long mincost;//最小花费 int maxflow;//最大流 long long totalcost; int totalflow; int S,T;//超级源点,超级汇点 while(~scanf("%d%d%d",&N,&M,&K)){ if(N==0&&M==0&&K==0)break; memset(totalneed,0,sizeof(totalneed)); memset(totalstorage,0,sizeof(totalstorage)); flag=true; totalcost=0; totalflow=0; for(int i=0;i<N;++i){ for(int j=0;j<K;++j){ scanf("%d",&need[i][j]); totalneed[j]+=need[i][j]; } } for(int i=0;i<M;++i){ for(int j=0;j<K;++j){ scanf("%d",&storage[i][j]); totalstorage[j]+=storage[i][j]; } } for(int i=0;i<K;++i){ if(totalneed[i]>totalstorage[i]){ flag=false; break; } } for(int k=0;k<K;++k){ MM.init(N+M+2);//初始化要放这里 for(int i=0;i<N;++i){ for(int j=0;j<M;++j){ scanf("%d",&cost[i][j]); MM.AddEdge(j,M+i,INF,cost[i][j]);//注意加边:j->M+i } } if(flag==false)continue; //超级源点 S=N+M; for(int v=0;v<M;++v){ MM.AddEdge(S,v,storage[v][k],0); } //超级汇点 T=N+M+1; for(int v=0;v<N;++v){ MM.AddEdge(M+v,T,need[v][k],0); } maxflow=MM.MincostMaxflow(S,T,mincost); if(maxflow<totalneed[k]) flag=false; totalcost+=mincost; totalflow+=maxflow; } if(flag==false)printf("-1\n"); else printf("%lld\n",totalcost); } return 0; }
2、SPFA版费用流:
/* SPFA版费用流 最小费用最大流,求最大费用最大流只需要取相反数,结果取相反数即可。 点的总数为N,点的编号0~N-1 */ #include<iostream> #include<stdio.h> #include<string.h> #include<queue> using namespace std; const int MAXN=10000; const int MAXM=100000; const int INF=0x3f3f3f3f; struct Edge{ int to,next,cap,flow,cost; }edge[MAXM]; int head[MAXN],tol; int pre[MAXN],dis[MAXN]; bool vis[MAXN]; int N;//节点总个数,节点编号从0~N-1 void init(int n){ N=n; tol=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int cap,int cost){ edge[tol].to=v; edge[tol].cap=cap; edge[tol].cost=cost; edge[tol].flow=0; edge[tol].next=head[u]; head[u]=tol++; edge[tol].to=u; edge[tol].cap=0; edge[tol].cost=-cost; edge[tol].flow=0; edge[tol].next=head[v]; head[v]=tol++; } bool spfa(int s,int t){ queue<int>q; for(int i=0;i<N;i++){ dis[i]=INF; vis[i]=false; pre[i]=-1; } dis[s]=0; vis[s]=true; q.push(s); while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=false; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost){ dis[v]=dis[u]+edge[i].cost; pre[v]=i; if(!vis[v]){ vis[v]=true; q.push(v); } } } } if(pre[t]==-1)return false; else return true; } //返回的是最大流,cost存的是最小费用 int minCostMaxflow(int s,int t,int &cost){ int flow=0; cost=0; while(spfa(s,t)){ int Min=INF; for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){ if(Min>edge[i].cap-edge[i].flow) Min=edge[i].cap-edge[i].flow; } for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){ edge[i].flow+=Min; edge[i^1].flow-=Min; cost+=edge[i].cost*Min; } flow+=Min; } return flow; } int main(){ int N,M,K;//汇点个数,源点个数,物品种类 int need[64][64];//need[i][j]表示第i个店主需要第j种商品的数量 int totalneed[64];//totalneed[i]表示第i种商品的总需求量 int storage[64][64];//storage[i][j]表示第i个仓库里存储的第j种商品的数量 int totalstorage[64];//totalstorage[i]表示第i种商品的总存储量 int cost[64][64];//cost[i][j]表示当前这个商品从第j个仓库运送到第i个店主的单位价格 bool flag;//false表示存储量不足 int mincost;//最小花费 int maxflow;//最大流 int totalcost; int totalflow; int S,T;//超级源点,超级汇点 while(~scanf("%d%d%d",&N,&M,&K)){ if(N==0&&M==0&&K==0)break; memset(totalneed,0,sizeof(totalneed)); memset(totalstorage,0,sizeof(totalstorage)); flag=true; totalcost=0; totalflow=0; for(int i=0;i<N;++i){ for(int j=0;j<K;++j){ scanf("%d",&need[i][j]); totalneed[j]+=need[i][j]; } } for(int i=0;i<M;++i){ for(int j=0;j<K;++j){ scanf("%d",&storage[i][j]); totalstorage[j]+=storage[i][j]; } } for(int i=0;i<K;++i){ if(totalneed[i]>totalstorage[i]){ flag=false; break; } } for(int k=0;k<K;++k){ init(N+M+2);//初始化要放这里 for(int i=0;i<N;++i){ for(int j=0;j<M;++j){ scanf("%d",&cost[i][j]); addedge(j,M+i,INF,cost[i][j]);//注意加边:j->M+i } } if(flag==false)continue; //超级源点 S=N+M; for(int v=0;v<M;++v){ addedge(S,v,storage[v][k],0); } //超级汇点 T=N+M+1; for(int v=0;v<N;++v){ addedge(M+v,T,need[v][k],0); } maxflow=minCostMaxflow(S,T,mincost); if(maxflow<totalneed[k]) flag=false; totalcost+=mincost; totalflow+=maxflow; } if(flag==false)printf("-1\n"); else printf("%d\n",totalcost); } return 0; }
3、zkw费用流:
/* zkw费用流 对于二分图类型的比较高效 */ #include<iostream> #include<stdio.h> #include<string.h> using namespace std; const int MAXN=128; const int MAXM=20000; const int INF=0x3f3f3f3f; struct Edge{ int to,next,cap,flow,cost; Edge(int _to=0,int _next=0,int _cap=0,int _flow=0,int _cost=0): to(_to),next(_next),cap(_cap),flow(_flow),cost(_cost){} }edge[MAXM]; struct ZKW_MinCostMaxFlow{ int head[MAXN],tot; int cur[MAXN]; int dis[MAXN]; bool vis[MAXN]; int ss,tt,N;//源点、汇点和点的总个数(编号是0~N-1),不需要额外赋值,调用会直接赋值 int min_cost,max_flow; void init(){ tot=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int cap,int cost){ edge[tot]=Edge(v,head[u],cap,0,cost); head[u]=tot++; edge[tot]=Edge(u,head[v],0,0,-cost); head[v]=tot++; } int aug(int u,int flow){ if(u==tt)return flow; vis[u]=true; for(int i=cur[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(edge[i].cap>edge[i].flow&&!vis[v]&&dis[u]==dis[v]+edge[i].cost){ int tmp=aug(v,min(flow,edge[i].cap-edge[i].flow)); edge[i].flow+=tmp; edge[i^1].flow-=tmp; cur[u]=i; if(tmp)return tmp; } } return 0; } bool modify_label(){ int d=INF; for(int u=0;u<N;u++) if(vis[u]) for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(edge[i].cap>edge[i].flow&&!vis[v]) d=min(d,dis[v]+edge[i].cost-dis[u]); } if(d==INF)return false; for(int i=0;i<N;i++) if(vis[i]){ vis[i]=false; dis[i]+=d; } return true; } /* 直接调用获取最小费用和最大流 输入:start-源点,end-汇点,n-点的总个数(编号从0开始) 返回值:pair<int,int>第一个是最小费用,第二个是最大流 */ pair<int,int> mincostmaxflow(int start,int end,int n){ ss=start,tt=end,N=n; min_cost=max_flow=0; for(int i=0;i<n;i++)dis[i]=0; while(1){ for(int i=0;i<n;i++)cur[i]=head[i]; while(1){ for(int i=0;i<n;i++)vis[i]=false; int tmp=aug(ss,INF); if(tmp==0)break; max_flow+=tmp; min_cost+=tmp*dis[ss]; } if(!modify_label())break; } return make_pair(min_cost,max_flow); } }solve; int main(){ int N,M,K;//汇点个数,源点个数,物品种类 int need[64][64];//need[i][j]表示第i个店主需要第j种商品的数量 int totalneed[64];//totalneed[i]表示第i种商品的总需求量 int storage[64][64];//storage[i][j]表示第i个仓库里存储的第j种商品的数量 int totalstorage[64];//totalstorage[i]表示第i种商品的总存储量 int cost[64][64];//cost[i][j]表示当前这个商品从第j个仓库运送到第i个店主的单位价格 bool flag;//false表示存储量不足 //int mincost;//最小花费 //int maxflow;//最大流 pair<int,int>pr; int totalcost; int totalflow; int S,T;//超级源点,超级汇点 while(~scanf("%d%d%d",&N,&M,&K)){ if(N==0&&M==0&&K==0)break; memset(totalneed,0,sizeof(totalneed)); memset(totalstorage,0,sizeof(totalstorage)); flag=true; totalcost=0; totalflow=0; for(int i=0;i<N;++i){ for(int j=0;j<K;++j){ scanf("%d",&need[i][j]); totalneed[j]+=need[i][j]; } } for(int i=0;i<M;++i){ for(int j=0;j<K;++j){ scanf("%d",&storage[i][j]); totalstorage[j]+=storage[i][j]; } } for(int i=0;i<K;++i){ if(totalneed[i]>totalstorage[i]){ flag=false; break; } } for(int k=0;k<K;++k){ solve.init();//初始化要放这里 for(int i=0;i<N;++i){ for(int j=0;j<M;++j){ scanf("%d",&cost[i][j]); solve.addedge(j,M+i,INF,cost[i][j]);//注意加边:j->M+i } } if(flag==false)continue; //超级源点 S=N+M; for(int v=0;v<M;++v){ solve.addedge(S,v,storage[v][k],0); } //超级汇点 T=N+M+1; for(int v=0;v<N;++v){ solve.addedge(M+v,T,need[v][k],0); } pr=solve.mincostmaxflow(S,T,N+M+2); if(pr.first<totalneed[k]) flag=false; totalcost+=pr.first; totalflow+=pr.second; } if(flag==false)printf("-1\n"); else printf("%d\n",totalcost); } return 0; }
时间: 2024-11-03 22:40:11