Time Limit: 4000MS | Memory Limit: 65536K | |
Total Submissions: 16091 | Accepted: 5641 |
Description
It's known that the cost to transport one unit goods for different kinds from different supply places to different shopkeepers may be different. Given each supply places' storage of K kinds of goods, N shopkeepers' order of K kinds of goods and the cost to transport goods for different kinds from different supply places to different shopkeepers, you should tell how to arrange the goods supply to minimize the total cost of transport.
Input
Then come K integer matrices (each with the size N * M), the integer (this integer is belong to (0, 100)) at the i-th row, j-th column in the k-th matrix represents the cost to transport one unit of k-th goods from the j-th supply place to the i-th shopkeeper.
The input is terminated with three "0"s. This test case should not be processed.
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
讲道理因为是放在最小费用最大流里面,所以很快就能建出模型了。希望以后碰到这样的题也能很快解决掉。
题意:有一个食品供应商D,在他的销售地区有N个店主(编号1~N)帮其销售产品.D有M个仓库(编号1~M),每个仓库可以提供K种不同的食品编号(1~K)(我程序直接从(0~K-1)。现在有店主向他订购食品,D应该怎么安排哪个仓库、向该店主提供多少食品,以减少总的费用?
输入:
多组输入。
第一行为3个整数N、M、K。接下来N行描述了每个店主的订量。接下来M行描述了每个仓库的各种食品的储存量,每行也是K个整数,代表每个仓库的美中食品的储存量。
接下来就是K个矩阵,每个矩阵大小是N*M,矩阵中所有整数的范围都是(0,100),第K个居中的第i行第j列的整数表示将单位重量的第K种物品从第J个仓库运往第I个店主所需的运输费用。
输出:
如果能满足所有的店主,那么输出最小花费,不能就输出-1.
思路:
首先就是求最小花费,然后对于每种物品有数量限制,如果把食品看成是流不难建成一个最小费用最大流的模。
难点就是它的食品有多种,而在我们求流的过程中,流本身是没有区别的。
这时候我们想一下,其实运输每种物品都是相互独立的,也就是说我们先运输第一种食品,然后第二种,....。每种物品其实是相互独立的,这样每种都求一个最小花费,那么最后加起来的总和就是我们要求的最小花费了。
建模:
超级源点就是0,然后是结点1~M表示仓库,然后是M+1~M+N表示N个店主,M+N+1表示超级汇点。
然后差不多是这样子:
注意:我们每次只求一种物品的,最后加和即可。
当某个物品不满足条件的时候直接输出-1即可。
之后就是连边。
1.超级源点到每个仓库,权值为0,流量为仓库的当前食品的数量。
2.每个仓库到每个店主连一条边,权值为矩阵中的花费,流量为无穷大(当前食品的数量也可以)。
3.每个店主到超级汇点连一条边,权值为0,流量为所需要的当前食品的数量。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int inf=1e9;
const int MAXN=50+5;
int n,m,k;
int s,e;//超级源点,超级汇点
int need[MAXN][MAXN];//店主需要的食品数量【编号】【种类】
int provide[MAXN][MAXN];//仓库能够提供的食品数量【编号】【数量】
int cost[MAXN][MAXN][MAXN];//矩阵【种类】【店主编号】←【仓库编号】
int sumflow[MAXN];//用来记录当前食品所需要的总数量
int cnt,head[110];
struct node
{
int u,v,w,f;
int next;
}edge[6000];
void add(int u,int v,int w,int f)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].f=f;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].u=v;
edge[cnt].v=u;
edge[cnt].w=-w;
edge[cnt].f=0;
edge[cnt].next=head[v];
head[v]=cnt++;
}
void build(int num)//num表示当前食品的种类
{
int i,j;
cnt=0;
for(i=0;i<=e;++i)head[i]=-1;
for(i=1;i<=n;++i)
{
for(j=1;j<=m;++j)
{
add(j,i+m,cost[num][i][j],provide[j][num]);
}
}
for(i=1;i<=m;++i)add(0,i,0,provide[i][num]);
for(i=1;i<=n;++i)add(i+m,e,0,need[i][num]);
}
int pre[110],dis[110];
bool vis[110];
bool spfa()
{
int i;
for(i=0;i<=e;++i)
{
dis[i]=inf;
vis[i]=0;
pre[i]=-1;
}
queue<int>q;
q.push(s);
dis[s]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
int w=edge[i].w;
int f=edge[i].f;
int t=dis[u]+w;
if(f>0&&dis[v]>t)
{
dis[v]=t;
pre[v]=i;
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
if(pre[e]==-1)return 0;
return 1;
}
int get_mincost(int num)
{
int ans=0;
int maxflow=0;
while(spfa())
{
int flow=inf;
int p=pre[e];
while(p!=-1)
{
flow=min(flow,edge[p].f);
p=pre[edge[p].u];
}
maxflow+=flow;
p=pre[e];
while(p!=-1)
{
edge[p].f-=flow;
edge[p^1].f+=flow;
ans+=edge[p].w*flow;
p=pre[edge[p].u];
}
}
if(maxflow<sumflow[num])return inf;//当前食品的种类满足不了条件
return ans;
}
int main()
{
int i,j,kk;
while(~scanf("%d%d%d",&n,&m,&k))
{
if(!n&&!m&&!k)break;
s=0;
e=n+m+1;
memset(sumflow,0,sizeof(sumflow));
for(i=1;i<=n;++i)
{
for(j=0;j<k;++j)
{
scanf("%d",&need[i][j]);
sumflow[j]+=need[i][j];
}
}
for(i=1;i<=m;++i)
{
for(j=0;j<k;++j)scanf("%d",&provide[i][j]);
}
for(kk=0;kk<k;++kk)
{
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)scanf("%d",&cost[kk][i][j]);
}
int ans=0;
for(i=0;i<k;++i)
{
build(i);
int t=get_mincost(i);
if(t==inf)
{
ans=-1;
break;
}
ans+=t;
}
printf("%d\n",ans);
}
return 0;
}