| Time Limit: 4000MS | Memory Limit: 65536K | |
| Total Submissions: 14275 | Accepted: 4895 |
Description
Input
Output
Sample Input
1 3 3 1 1 1 0 1 1 1 2 2 1 0 1 1 2 3 1 1 1 2 1 1 1 1 1 3 2 20 0 0 0
Sample Output
4 -1
Source
大致题意:
有N个供应商,M个店主,K种物品。每个供应商对每种物品的的供应量已知,每个店主对每种物品的需求量的已知,从不同的供应商运送不同的货物到不同的店主手上需要不同的花费,又已知从供应商Mj送第kind种货物的单位数量到店主Ni手上所需的单位花费。
问:供应是否满足需求?如果满足,最小运费是多少?
解题思路:
费用流问题。
(1)输入格式
在说解题思路之前,首先说说输入格式,因为本题的输入格式和解题时所构造的图的方向不一致,必须要提及注意。以样例1为例:

(2)题目分析和拆解:
A、首先处理“供应是否满足需求”的问题。
要总供应满足总需求,就必须有 每种物品的供应总量都分别满足其需求总量,只要有其中一种物品不满足,则说明供不应求,本组数据无解,应该输出-1。但是要注意这里判断无解后,只能做一个标记,但还要继续输入,不然一旦中断输入,后面的几组数据结果就全错了。
而要知道“每种物品的供应总量都分别满足其需求总量”,对所有供应商第kind种物品的供应量求和ksupp[kind],对所有店主第kind种物品的需求量求和kneed[kind],然后比较ksupp[kind]与kneed[kind]就可以了。
而最小费用流的计算是建立在“供等于求”或“供过于求”的基础上的。
B、最小费用问题
要直接求出“把所有物品从所有供应商运送到所有店主的最小费用MinTotalCost”是不容易的。但是求出“把第kind种物品从所有供应商运送到所有店主的最小费用MinCost[kind]”却简单得多,这就转化为经典的多源多汇的费用流问题,而最后只需要把K种物品的最小费用求和 MinCost[kind],就能得到运送所有物品的最小费用MinTotalCost。
其实题目的输入方式最后要输入K个矩阵已经暗示了我们要拆解处理。
C、构图
那么对于第kind种物品如何构图呢?
解决多源多汇网络问题,必须先构造与其等价的单源单汇网络。构造超级源s和超级汇t,定义各点编号如下:
超级源s编号为0,供应商编号从1到M,店主编号从M+1到M+N,超级汇t编号为M+N+1。
令总结点数Nump=M+N+2,申请每条边的“花费”空间cost[Nump][ Nump]和“容量”空间cap[Nump][ Nump],并初始化为全0。
超级源s向所有供应商M建边,费用为0,容量为供应商j的供应量。
每个供应商都向每个店主建边,正向弧费用为输入数据的第kind个矩阵(注意方向不同),容量为供应商j的供应量;反向弧费用为正向弧费用的负数,容量为0。
所有店主向超级汇t建边,费用为0,容量为店主i的需求量。
注意:1、其他没有提及的边,费用和容量均为0,容量为0表示饱和边或不连通。
2、计算每种物品的最小费用都要重复上述工作重新构图,不过存储空间cost和cap不必释放,可重新赋值再次利用。
#include<map>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<climits>
#include<list>
#include<iomanip>
#include<stack>
#include<set>
using namespace std;
int head[110],tail;
struct Edge
{
int from,to,cap,flow,cost,next;
}edge[10000000];
void add(int from,int to,int cap,int flow,int cost)
{
edge[tail].from=from;
edge[tail].to=to;
edge[tail].cap=cap;
edge[tail].flow=flow;
edge[tail].cost=cost;
edge[tail].next=head[from];
head[from]=tail++;
}
int dis[110],p[110],re[110],ed;
bool iq[110];
bool bf(int &flow,int &cost)
{
fill(dis,dis+ed+1,INT_MAX);
memset(iq,0,sizeof(iq));
dis[0]=0;
iq[0]=1;
re[0]=INT_MAX;
queue<int>qq;
qq.push(0);
while(qq.size())
{
int from=qq.front();
qq.pop();
iq[from]=0;
for(int i=head[from];i!=-1;i=edge[i].next)
{
Edge &e=edge[i];
if(e.cap>e.flow&&dis[e.to]>dis[from]+e.cost)
{
dis[e.to]=dis[from]+e.cost;
p[e.to]=i;
re[e.to]=min(re[from],e.cap-e.flow);
if(!iq[e.to])
{
qq.push(e.to);
iq[e.to]=1;
}
}
}
}
if(dis[ed]==INT_MAX)
return 0;
flow+=re[ed];
cost+=dis[ed]*re[ed];
int t=ed;
while(t!=0)
{
edge[p[t]].flow+=re[ed];
edge[p[t]^1].flow-=re[ed];
t=edge[p[t]].from;
}
return 1;
}
int mincost(int goal)
{
int flow=0,cost=0;
while(bf(flow,cost));
if(flow!=goal)
return -1;
return cost;
}
int a[60][60],b[60][60],c[60][60][60];
int main()
{
int n,m,k;
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
if(n==0&&m==0&&k==0)
return 0;
for(int i=1;i<=n;i++)
for(int j=1;j<=k;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=m;i++)
for(int j=1;j<=k;j++)
scanf("%d",&b[i][j]);
for(int i=1;i<=k;i++)
for(int j=1;j<=n;j++)
for(int ii=1;ii<=m;ii++)
scanf("%d",&c[i][j][ii]);
ed=n+m+1;
int ans=0;
for(int i=1;i<=k;i++)
{
tail=0;
memset(head,-1,sizeof(head));
for(int ii=1;ii<=m;ii++)
{
add(0,ii,b[ii][i],0,0);
add(ii,0,0,0,0);
for(int jj=1;jj<=n;jj++)
{
add(ii,m+jj,b[ii][i],0,c[i][jj][ii]);
add(m+jj,ii,0,0,-c[i][jj][ii]);
}
}
int goal=0;
for(int ii=1;ii<=n;ii++)
{
goal+=a[ii][i];
add(m+ii,ed,a[ii][i],0,0);
add(ed,m+ii,0,0,0);
}
int t=mincost(goal);
if(t==-1)
{
ans=-1;
break;
}
ans+=t;
}
printf("%d\n",ans);
}
}原文:http://blog.csdn.net/stl112514/article/details/44812483