Language:
Minimum Cost
Time Limit: 4000MS   Memory Limit: 65536K
Total Submissions: 14334   Accepted: 4908

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

一道最小费用最大流的题,难点在建图,不是建图难,而是坑,不能一次性建图,这样会超时,感觉这题的我写的代码上面的注释还是不错的,适合小白去看。

 

#include<iostream>
#include<cstring>
#include<cstdio>
#include<climits>
#include<queue>
#define MAXN 110
using namespace std;
struct Edge
{
    int s,t,f,c,next;
}edge[12000];//构造邻接表
int N,M,K;
int head[MAXN];//构造邻接表
int pre[MAXN];//纪录路径
bool isq[MAXN];//纪录一个点是否在队列里面
int dist[MAXN];//纪录到该点的最小费用
int store[55][55];//商店的需求量
int market[55][55];//市场的供应量
int good[55][55][55];//从市场到商店的费用
int SUM[55];//所有的商店对第i件商品的需求量之和
int ALL[50];//所有的市场对第j件商品的供给量之和
int ent;//表示边的编号
int s,t;//表示超级源点超级汇点
int cost;//表示最小的费用
void add(int s,int t,int f,int c)
{
    edge[ent].s=s;
    edge[ent].t=t;
    edge[ent].f=f;
    edge[ent].c=c;
    edge[ent].next=head[s];
    head[s]=ent++;
    edge[ent].s=t;
    edge[ent].t=s;
    edge[ent].f=0;
    edge[ent].c=-c;
    edge[ent].next=head[t];
    head[t]=ent++;
}
int MIN(int a,int b)
{
    return a<b?a:b;
}
bool spfa()
{
    memset(pre,-1,sizeof(pre));//初始化路径为-1
    for(int i=s;i<=t;i++)
    {
        isq[i]=false;//每点开始时均不在队列里面
        dist[i]=INT_MAX;//初始化到每点的最小费用均为INT_MAX
    }
    queue<int>q;
    q.push(s);
    isq[s]=true;//源点s已经放入到队列里面去了
    dist[s]=0;//从源点到源点的距离出回话为0
    while(!q.empty())//当队列为空时优化过程结束,退出循环
    {
        int temp1=q.front();
        q.pop();
        isq[temp1]=false;//该点已经退出队列
        for(int i=head[temp1];i!=-1;i=edge[i].next)//从该点找通过邻接表找所有的以该点为起点的边,从中找出能优化的点
        {
            int temp2=edge[i].t;
            if(edge[i].f&&dist[temp2]>dist[temp1]+edge[i].c)
            {
                dist[temp2]=dist[temp1]+edge[i].c;
                pre[temp2]=i;
                if(!isq[temp2])//如果该点不在队列中,则将该点放入队列中
                {
                    q.push(temp2);
                    isq[temp2]=true;
                }
            }
        }
    }
    return pre[t]!=-1;//如果pre[t]==-1的话说明没有找到从s到t的路径,即已经找到所有的路径了,结束循环
}
int MCMF()
{
    int cost=0;
    while(spfa())
    {
        int minn=INT_MAX;//为找到这个过程中能流过的最大流所加的变量
        for(int i=pre[t];i!=-1;i=pre[i])
        {
            minn=MIN(minn,edge[i].f);
            i=edge[i].s;
        }
        for(int i=pre[t];i!=-1;i=pre[i])
        {
            edge[i].f-=minn;//将该条路径经过的边的流量都减掉该过程能经过的最大流
            edge[i^1].f+=minn;//反向边加上该最大流,从而保证可以反悔
            i=edge[i].s;
        }
        cost+=minn*dist[t];//把每次找到的费用加起来
    }
    return cost;//返回最小费用
}
int main()
{
    int temp,ok;
    while(cin>>N>>M>>K,N+M+K)
    {
        cost=0;
        bool ok=true;//纪录市场能不能满足超市的需求
        memset(SUM,0,sizeof(SUM));
        memset(ALL,0,sizeof(ALL));
        for(int i=1;i<=N;i++)
        {
            for(int j=1;j<=K;j++)
            {
                scanf("%d",&store[i][j]);//第i家商店对第j件商品的需求量
                SUM[j]+=store[i][j];
            }
        }
        for(int i=1;i<=M;i++)
        {
            for(int j=1;j<=K;j++)
            {
                scanf("%d",&market[i][j]);//第i家供货商对第j件商品的供给量
                ALL[j]+=market[i][j];
            }
        }
        for(int i=1;i<=K;i++)
        {
            for(int j=1;j<=N;j++)
            {
                for(int k=1;k<=M;k++)
                {
                    scanf("%d",&good[i][j][k]);//第k家供货商给第j家商店供应第i件商品的单价
                }
            }
        }
        for(int i=1;i<=K;i++)
        {
            if(SUM[i]>ALL[i])
            {
                ok=false;
                break;
            }
        }
        if(ok==false)
        {
            printf("-1\n");//供应满足不了需求,输出-1
        }
        else
        {
            for(int i=1;i<=K;i++)//对第i件物品使用最小费用最大流算法
            {
                ent=0;//初始化边
                memset(head,-1,sizeof(head));//初始化邻接表
                for(int j=1;j<=M;j++)
                {
                    add(0,j,market[j][i],0);//从源点到供货商的边
                }
                for(int j=1;j<=M;j++)
                {
                    for(int k=1;k<=N;k++)
                    {
                        add(j,k+M,INT_MAX,good[i][k][j]);//从供货商到商店的边
                    }
                }
                for(int j=1;j<=N;j++)
                {
                    add(j+M,M+N+1,store[j][i],0);//从商店到汇点的边
                }
                s=0;
                t=M+N+1;
                cost+=MCMF();
            }
            printf("%d\n",cost);
        }
    }
}