| 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