poj2516 Minimum Cost

Minimum Cost
Time Limit: 4000MS Memory Limit: 65536K
Total Submissions: 16091 Accepted: 5641

Description

Dearboy, a goods victualer, now comes to a big problem, and he needs your help. In his sale area there are N shopkeepers (marked from 1 to N) which stocks goods from him.Dearboy has M supply places (marked from 1 to M), each provides K different kinds of goods (marked from 1 to K). Once shopkeepers order goods, Dearboy should arrange which supply place provide how much amount of goods to shopkeepers to cut down the total cost of transport. 

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

The input consists of multiple test cases. The first line of each test case contains three integers N, M, K (0 < N, M, K < 50), which are described above. The next N lines give the shopkeepers' orders, with each line containing K integers (there integers are belong to [0, 3]), which represents the amount of goods each shopkeeper needs. The next M lines give the supply places' storage, with each line containing K integers (there integers are also belong to [0, 3]), which represents the amount of goods stored in that supply place. 

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

For each test case, if Dearboy can satisfy all the needs of all the shopkeepers, print in one line an integer, which is the minimum cost; otherwise just output "-1".

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;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值