最小费用最大流+(对最小费用最大流的理解)
题目链接:https://cn.vjudge.net/contest/68128#problem/E
具体思路:图的建立方式, 超级源点 - > 供应商 - > 顾客 - > 超级汇点。
因为有多种商品,所以可以简化构图过程,每一次求一种商品的最小费用最大流,然后最终将所有商品的最小费用最大流的总和加起来就可以了,注意条件在代码中解释。
注意: 对于源点到供应商这一段不能设置成inf,具体例子如下所示。
s - >s1,同时s1 - >s2,s1 - > s3。这样的话,就相当于有两条路从s出发,本来s到s1的流量总和为5,但是如果把流量设置成inf,
那么如果本来s1 - >s2,s1 - > s3这两条路的和是超过5的,也就是说按照原来的建图方式,初始从s-> s1 的流量是不够用的,如果改成inf的话,流量就够用,那么就改变题意了。
AC代码:
#include<iostream>
#include<string>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<stdio.h>
using namespace std;
# define inf 0x3f3f3f3f
# define maxn 150+100
# define ll long long
int need[maxn][maxn];
int store[maxn][maxn];
int Map[maxn][maxn][maxn];// i - > numofstore ,j- > huowu ,k - > keeper
int sum1[maxn],sum2[maxn];
int head[maxn];
int dis[maxn],prev[maxn],pree[maxn];
int vis[maxn];
int num;
struct Edge
{
int to;
int cost;
int w;
int nex;
} edge[maxn];
void addage(int fr,int to,int w,int cost)
{
edge[num].to=to;
edge[num].w=w;
edge[num].cost=cost;
edge[num].nex=head[fr];
head[fr]=num++;
edge[num].to=fr;
edge[num].w=0;
edge[num].cost=-cost;
edge[num].nex=head[to];
head[to]=num++;
}
bool spfa(int st,int ed)
{
memset(vis,0,sizeof(vis));
memset(pree,-1,sizeof(pree));
memset(dis,inf,sizeof(dis));
dis[st]=0;
vis[st]=1;
queue<int>q;
q.push(st);
while(!q.empty())
{
int top=q.front();
q.pop();
vis[top]=0;
for(int i=head[top]; i!=-1; i=edge[i].nex)
{
int temp=edge[i].to;
if(edge[i].w>0&&dis[temp]>dis[top]+edge[i].cost)
{
dis[temp]=dis[top]+edge[i].cost;
pree[temp]=top;
prev[temp]=i;
if(vis[temp]==0)
{
vis[temp]=1;
q.push(temp);
}
}
}
}
return pree[ed]!=-1;
}
int mincostflow(int st,int ed)
{
int ans=0;
while(spfa(st,ed))
{
int minn=inf;
for(int i=ed; i!=st; i=pree[i])
{
minn=min(minn,edge[prev[i]].w);
}
ans+=minn*dis[ed];//dis代表一个单位的花费,minn代表流量。
for(int i=ed; i!=st; i=pree[i])
{
edge[prev[i]].w-=minn;
edge[prev[i]^1].w+=minn;
}
}
return ans;
}
int main()
{
int n,m,k;
while(~scanf("%d%d%d",&n,&m,&k)&&(n+m+k))
{
int flag=0;
memset(need,0,sizeof(need));
memset(sum1,0,sizeof(sum1));
memset(sum2,0,sizeof(sum2));
memset(Map,0,sizeof(Map));
for(int i=1; i<=n; i++)
{
for(int j=1; j<=k; j++)
{
scanf("%d",&need[i][j]);
sum1[j]+=need[i][j];
}
}// xu qiu liang
for(int i=1; i<=m; i++)
{
for(int j=1; j<=k; j++)
{
scanf("%d",&store[i][j]);
sum2[j]+=store[i][j];
}
}
for(int i=1; i<=k; i++)
{
if(sum1[i]>sum2[i])//判断能否满足构图的条件
{
flag=1;
}
}
for(int h=1; h<=k; h++)
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
scanf("%d",&Map[h][j][i]);// 第j种货物从j仓库到达第i个买家的花费
}
}
}
if(flag==1)printf("-1\n");
else
{
int ans=0;
for(int h=1; h<=k; h++)
{
memset(head,-1,sizeof(head));
num=0;
for(int i=1; i<=m; i++)
{
addage(0,i,store[i][h],0);//注意这里的流量设置,不能设置成inf。
}
for(int i=1; i<=m; i++)
{
for(int j=1; j<=n; j++)
{
addage(i,j+m,inf,Map[h][i][j]);//这里的流量即可以设置成inf,也可以设置成存货。
}
}
for(int i=1; i<=n; i++)
{
addage(m+i,n+m+1,need[i][h],0);//这里设置成需求量。
}
ans+=mincostflow(0,n+m+1);
}
printf("%d\n",ans);
}
}
return 0;
}