题意:
看了很久,,,好无语的输入。。。还是借鉴下网上的意思吧,比我说得清楚多了,可以转化为一道最小费用流问题
@小優YoU
http://blog.csdn.net/lyy289065406/article/details/6742534
//poj2516
//Memory: 952K
//Time: 360MS
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
using namespace std;
const int INF = 1e9+9;
const int MAXN = 105;
struct Node
{
int from; //左
int to; //右
int cap; //容量
int flow; //流量
int cost; //费用
};
vector <Node> e; ///一条边的所有属性
vector <int> v[MAXN]; ///记录边的位置
int vis[MAXN];
int dis[MAXN];
int father[MAXN],pos[MAXN]; ///father保存父节点, pos保存cap
void Clear(int x)
{
for(int i=0; i<=x; ++i)
v[i].clear();
e.clear();
}
void add_Node(int from, int to, int cap, int cost)
{
e.push_back((Node){from,to,cap,0,cost});
e.push_back((Node){to,from,0,0,-cost}); ///反向边
int len = e.size()-1;
v[to].push_back(len);
v[from].push_back(len-1);
}
bool SPFA(int s, int t, int& flow, int& cost)
{
for(int i=0; i<MAXN; ++i)
dis[i] = INF;
memset(vis,0,sizeof(vis));
dis[s] = 0; //最短路径,即 最小费用
vis[s] = 1; //标记是否在队列里
father[s] = 0; //p保存father,
pos[s] = INF; //pos保存cap
queue<int> q;
q.push(s);
while(!q.empty())
{//cout<<"1"<<endl;
int u = q.front();
q.pop();
vis[u] = 0;
for(int i=0; i<v[u].size(); ++i)
{
Node &tmp = e[v[u][i]]; /
if(tmp.cap> tmp.flow && dis[tmp.to]>dis[u]+tmp.cost)
{//cout<<u<<" "<<tmp.to<<" "<<dis[t]<<endl;
dis[tmp.to] = dis[u] + tmp.cost;
father[tmp.to] = v[u][i]; ///哦? 头节点的设置 ,方便从t到s 的更新
pos[tmp.to] = min(pos[u], tmp.cap-tmp.flow); ///更新边的剩余容量(最大流量),记录最小的
if(!vis[tmp.to])
{
q.push(tmp.to);
vis[tmp.to] = 1;
}
}
}
}
if(dis[t]==INF) return false;
flow += pos[t]; ///总(扩增)流量。。
cost += dis[t]*pos[t]; ///???费用的计算方法,要好好理解。。。
int t2 = t;
while(t2 != s) //回去???
{
e[father[t2]].flow += pos[t]; //更新流量,为了下次寻找新的最小费用
e[father[t2]^1].flow -= pos[t]; //反向边
t2 = e[father[t2]].from;
}
return true;
}
int Min_Cost(int s,int t)
{
int flow = 0;
int cost = 0;
while(SPFA(s,t,flow,cost));
return cost;
}
int need[MAXN][MAXN]; //i需求者需要need[i][j]个第j个物品
int have[MAXN][MAXN]; //i供应站有have[i][j]个第j个物品
int all_n[MAXN];
int all_h[MAXN];
int main()
{
///题目是双向边,那么反向边会不会造成混淆呢???
///
int N,M,K;
while(scanf("%d%d%d",&N,&M,&K))
{
if(!N&&!M&&!K) break;
// Clear(MAXN);
memset(need,0,sizeof(need));
memset(have,0,sizeof(have));
memset(all_n,0,sizeof(all_n));
memset(all_h,0,sizeof(all_h));
//
for(int i=1; i<=N; ++i)
for(int j=1; j<=K; ++j)
{
scanf("%d",&need[i][j]);
all_n[j] += need[i][j];
}
for(int i=1; i<=M; ++i)
for(int j=1; j<=K; ++j)
{
scanf("%d",&have[i][j]);
all_h[j] += have[i][j];
}
int flag = 0;
for(int j=1; j<=K; ++j)
if(all_h[j]<all_n[j]) {flag=1; break;}
int ans = 0;
int value;
for(int k=1; k<=K; ++k)
{
Clear(MAXN);
for(int i=1; i<=N; ++i)
for(int j=1; j<=M; ++j)
{
scanf("%d",&value);
if(flag) continue;
add_Node(j,M+i,have[j][k],value);///
}
if(flag) continue;
for(int p=1; p<=M; ++p)
add_Node(0,p,have[p][k],0);///
for(int q=1; q<=N; ++q)
add_Node(M+q,M+N+1,need[q][k],0);///
int cost = Min_Cost(0,M+N+1);
ans += cost;
}
if(flag) printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}