#include "stdio.h" // 最小费用最大流 poj 2516 #include "string.h" #include "queue" using namespace std; #define N 115 #define INF 1000000000 struct node { int u,v,w,k; int next; }edge[8*N*N]; int store[N]; int n,m,k,ans,idx; int head[N],dis[N],route[N],mark[N]; int shop[N][N],supply[N][N],cost[N][N]; void init(); void EK(int start,int end); int SPFA(int start,int end); void adde(int u,int v,int w,int k); void addedge(int u,int v,int w,int k); int main() { int sum; int i,j,tt; int sum1[N],sum2[N]; while(scanf("%d %d %d",&n,&m,&k)) { tt=k; if(n==0 && m==0 && k==0) return 0; memset(sum1,0,sizeof(sum1)); memset(sum2,0,sizeof(sum2)); for(i=1;i<=n;i++) { for(j=1;j<=k;j++) { scanf("%d",&shop[i][j]); sum1[j]+=shop[i][j]; //N家商店需要第j件物品的总件数 } } for(i=1;i<=m;i++) { for(j=1;j<=k;j++) { scanf("%d",&supply[i][j]); sum2[j]+=supply[i][j]; //M处仓库供应第j件物品的总件数 } } sum = 0; //答案初始化(sum记录结果) bool flag=true; for(tt=1;tt<=k;tt++) { if(sum1[tt]>sum2[tt]) flag=false; init(); int start=0,end=n+m+1; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { scanf("%d",&cost[i][j]); adde(j,i+m,cost[i][j],INF); } } for(j=1;j<=m;j++) adde(start,j,0,supply[j][tt]); for(i=1;i<=n;i++) { store[i] = idx; adde(i+m,end,0,shop[i][tt]); } while(SPFA(start,end)) EK(start,end); sum += ans; } if(flag==false) printf("-1\n"); else printf("%d\n",sum); } return 0; } void init() { ans = 0; idx = 0; memset(head,-1,sizeof(head)); } void adde(int u,int v,int w,int k) //对其中的一条再加上一条流量为0的回路 { addedge(u,v,w,k); addedge(v,u,-w,0); } void addedge(int u,int v,int w,int k) //邻接表建边 { edge[idx].u = u; edge[idx].v = v; edge[idx].w = w; edge[idx].k = k; edge[idx].next = head[u]; head[u] = idx; idx++; } int SPFA(int start,int end) //找一条存在流量的最小费用流(存在流量就行) { int i; memset(mark,false,sizeof(mark)); //初始化标记数组mark[]; memset(route,-1,sizeof(route)); //ruote[]记录流量路径(存下一条边的下标) for(i=start;i<=end;i++) dis[i] = INF; dis[start] = 0; queue<int> q; q.push(start); mark[start] = true; int x,y; while(!q.empty()) { x = q.front(); for(i=head[x];i!=-1;i=edge[i].next) { y = edge[i].v; if(edge[i].k && dis[y] > dis[x] + edge[i].w) { dis[y] = dis[x] + edge[i].w; route[y] = i; //route[]里面存的为边的下标 if(mark[y] == false) { mark[y] = true; q.push(y); } } } q.pop(); mark[x] = false; //对出队列的点的标记还原 } if(dis[end] == INF) return 0; return 1; } void EK(int start,int end) { int s=INF; int x,y; y = route[end]; while(y!=-1) { if(s>edge[y].k) s = edge[y].k; y = route[edge[y].u]; } y = route[end]; while(y!=-1) { x = y^1; //很特别的处理; edge[y].k -= s; //对流量进行处理(正减反加) edge[x].k += s; ans += edge[y].w*s; y = route[edge[y].u]; //通过route[]访问路径上的下一个节点 } }