这道题用了spa算法,多源点多汇点最大流,最小费用最大流
题意蛮复杂的:
转载请注明出处:優YoU http://blog.csdn.net/lyy289065406/article/details/6742534
大致题意:
有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不必释放,可重新赋值再次利用。
D、求解
对于第kind种物品的图,都用spfa算法求解最小费用路径(增广链),再利用可分配最大流调MaxFlow整增广链上的容量,正向弧容量减去MaxFlow,反向弧容量减去MaxFlow,费用为单位花费乘以MaxFlow。
具体的算法流程可参考我POJ2195的解题报告,基本一样。但注意的导致本题无可行解的原因只有“供不应求”,由输入数据知显然各边的容量均>=0,因此并不会出现负权环,spfa仍然用while循环直至无增广链为止足矣。
代码:
(侵删)
建图,对每个仓库是一个结点,每个客户也是一个结点,除此之外再加上s源点和t结束点!
1、s到仓库i的边的权值为仓库i的供给量
2、仓库i到客户j的权值为仓库的供给量
3、客户j到t的权值为客户的需求量
4、还有建立花费图,cost[i][j]代表仓库i到客户j的没一个物品的运费
然后用spfa算法求s到t的最小路径(路径上边的权值不为0),如果到t的最短路径有更新(此条路径话费最少),则记录这条路径,求出这条路径上权值最小的边的权值,然后这条路径上所有的边的权值都减去该边的权值min,此时最小的花费增加了min*在这条边上的花费!如果s到t的值没有更新,则对于k物品的最小花费则已经求出来了!
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<string.h>
#include<cmath>
#include<queue>
using namespace std;
#define min(a,b) (a)<(b)?(a):(b)
#define N 130
#define inf 100000000
#define INT_MAX 10000000
int need[N][N];
int offer[N][N];
int map[N][N],cost[N][N];
int sum_need[N],sum_offer[N];
int n,m,K;
int pre[N];
int s,t;//源点和结束点
int spfa()
{
int dis[N];
bool vis[N];
int i;
queue<int>q;
for(i=0; i<=t; i++)
{
vis[i]=false;
dis[i]=inf;
}
vis[s]=true;
dis[s]=0;
q.push(s);
while(!q.empty())
{
int k=q.front();
vis[k]=false;
q.pop();
for(i=0; i<=t; i++)
{
if(map[k][i]&&dis[i]>dis[k]+cost[k][i])
{
dis[i]=dis[k]+cost[k][i];
pre[i]=k;
if(vis[i]==false)
{
vis[i]=true;
q.push(i);
}
}
}
}
if(dis[t]!=inf)
return 1;
else
return 0;
}
int fond()
{
int i,j;
int Min=INT_MAX;
j=0;
while(spfa())
{
for(i=t; i!=s; i=pre[i])
Min=min(Min,map[pre[i]][i]);
for(i=t; i!=s; i=pre[i])
{
map[pre[i]][i]-=Min;
map[i][pre[i]]+=Min;
j+=cost[pre[i]][i]*Min;
}
}
return j;
}
int main()
{
int i,j,k,sum,sign;
while(scanf("%d%d%d",&n,&m,&K))
{
if(n==0&&m==0&&K==0)
break;
sum=0;
memset(sum_need,0,sizeof(sum_need));
memset(sum_offer,0,sizeof(sum_offer));
for(i=1; i<=n; i++)
for(j=1; j<=K; j++)
{
scanf("%d",&need[i][j]);
sum_need[j]+=need[i][j];
}
for(i=1; i<=m; i++)
for(j=1; j<=K; j++)
{
scanf("%d",&offer[i][j]);
sum_offer[j]+=offer[i][j];
}
sign=0;
for(i=1; i<=K; i++)
if(sum_offer[i]<sum_need[i])
{
sign=1;
break;
}
s=0;
t=m+n+1;
for(k=1; k<=K; k++)
{
for(i=0; i<=t; i++)
for(j=0; j<=t; j++)
map[i][j]=cost[i][j]=0;
for(i=1+m; i<=n+m; i++)
for(j=1; j<=m; j++)
{
scanf("%d",&cost[j][i]);
cost[i][j]=-cost[j][i];
}
if(sign==1)
continue;
for(i=1; i<=m; i++)
map[s][i]=offer[i][k];
for(i=1; i<=m; i++)
for(j=1+m; j<=n+m; j++)
map[i][j]=offer[i][k];
for(i=m+1; i<=m+n; i++)
map[i][t]=need[i-m][k];
sum+=fond();
}
if(sign==1)
printf("-1\n");
else
printf("%d\n",sum);
}
return 0;
}