Minimum Cost

 

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

 

这个题意就是有n(商店),m(提供商),k(商品),然后接着分别输入n*k,代表每个商店所需要的k商品的数量,然后是m*k代表的是每个提供商拥有的k商品的个数,接着是k个n*m,代表每个商品由提供商到商店所需的消耗,所以这个题就是让求最小的耗费,这个题要把k个商品分开来求,最后再求和。建图方式注释详见

#include <iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#define oo 1<<28
#include<queue>
#include<algorithm>
using namespace std;
int pre[210000];
int dis[210000];
int vist[210000];
int head[210000];
int a[100000],b[100000];
int need[1000][1000],have[1000][1000],money[500][500][500];
struct node
{
    int u;
    int v;//与u点相连的点
    int f;//容量
    int w;//权值
    int next;//下一条边
} edge[1100000];
int cnt=0;
int s,t;
void add(int u,int v,int f,int w)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].f=f;
    edge[cnt].w=w;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].u=v;
    edge[cnt].v=u;
    edge[cnt].f=0;
    edge[cnt].w=-w;//反向的权值为正向的相反数
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
void init()
{
    memset(pre,-1,sizeof(pre));
    memset(vist,0,sizeof(vist));
    for(int i=0; i<=t; i++)
    {
        dis[i]=oo;
    }
}
int spfa()
{
    int i;
    init();
    queue<int>q;
    dis[s]=0;
    vist[s]=1;
    q.push(s);
    while(!q.empty())//将相邻点进行松弛,直到队列为空
    {
        int u=q.front();//取出队头
        q.pop();
        i=head[u];
        vist[u]=0;
        while(i!=-1)
        {
            int w=edge[i].w;
            int v=edge[i].v;
            if(edge[i].f>0&&dis[v]>dis[u]+w)//判断是否可以更新
            {
                dis[v]=dis[u]+w;//改进s到v点的值
                pre[v]=i;//记录此点的前驱
                if(!vist[v])//由于距离变小了,如果v点松弛成功且v点不在队列里,因为v点有可能还能改进别的点
                {
                    vist[v]=1;
                    q.push(v);
                }
            }
            i=edge[i].next;
        }
    }
    if(pre[t]==-1)//如果不在最短路中代表寻找失败
        return 0;
    return 1;
}
int Cost()
{
    int ans=0;
    while(spfa())//如果增广路寻找成功
    {
        int maxx=oo;
        int p=pre[t];//初始化P指针
        while(p!=-1)
        {
            maxx=min(edge[p].f,maxx);//求出此增广路上边的最小值
            p=pre[edge[p].u];
        }
        p=pre[t];
        while(p!=-1)
        {
            edge[p].f-=maxx;//正向减去
            edge[p^1].f+=maxx;//反向增加
            ans+=maxx*edge[p].w;//因为以单位计费,所以应当乘上流量
            p=pre[edge[p].u];
        }
    }
    return ans;
}
int main()
{
    int n,m,d,k;
    int i,j,l;
    while(~scanf("%d%d%d",&n,&m,&k),n||m||k)
    {
        int u,v,w;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=k; j++)
            {
                scanf("%d",&need[j][i]);
                a[j]+=need[j][i];//每个商品商店一共需要多少
            }
        }
        for(i=1; i<=m; i++) //每一个货物
        {
            for(j=1; j<=k; j++)
            {
                scanf("%d",&have[j][i]);
                b[j]+=have[j][i];//每个商品提供商一共拥有多少
            }
        }
        for(i=1; i<=k; i++)
        {
            for(j=1; j<=n; j++)
            {
                for(l=1; l<=m; l++)
                {
                    scanf("%d",&money[i][l][j]);//第k件物品由运货商到商店需要的代价
                }
            }
        }
        int flag=1;
        for(i=1;i<=k;i++)
        {
            if(b[i]<a[i])//若k种商品提供商拥有的比需要量小
            {
                flag=0;
                break;//则直接退出,因为k种商品不满足
            }
        }
        if(flag==0)
        {
            printf("-1\n");
        }
        else
        {
            int sum=0;
            
            for(i=1;i<=k;i++)//分成k个商品分别求最小费用
            {
                memset(head,-1,sizeof(head));
                for(j=1;j<=m;j++)
                {
                    add(0,j,have[i][j],0);//将源点和运货商连,容量为第i种物品第j个运销商所拥有个个数,权值为0
                }
                for(j=1;j<=n;j++)
                {
                    add(j+m,m+n+1,need[i][j],0);//将商店和汇点相连,容量为第i种物品第j个商品所需要的个数,权值为0
                }
                for(j=1;j<=m;j++)
                {
                    for(l=1;l<=n;l++)
                    {
                        add(j,l+m,oo,money[i][j][l]);//将运销商和商店连,容量为无穷,因为不知多大,权值为第i件物品第j个运销商到第l个商店所需要费用
                    }
                }
                s=0,t=n+m+1;
                int e=Cost();
                sum+=e;
            }
            printf("%d\n",sum);
        }
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值