分析:
这题我马上想到的是拆点
开始做的时候直接拆了nk+mk个点,这样做是不行的
正解是把k种商品独立,轮流对每个商品建图,
跑k次最小费用最大流,把每次跑出来的费用相加就行了
不需要拆点。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<algorithm>
#include<map>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int N=400;
const int M=6e5+5;
struct Node{
int from,to,nt,cap,flow,cost;
}e[M];
int head[N];
int cnt;
int d[N];
int mark[N];
int pre[N];
int n,m,k;
int s,t;
int mincost,maxflow;//总货物数量和总花费。
int need[N][N];//保存每个人需要的每个商品数量
int have[N][N];//保存每个供应商有的每个商品的数量
int kcost[N][N][N];//保存成本花费
void init(){
cnt=-1;
memset(head,-1,sizeof head);
s=0;
t=n+m+1;
}
void add(int a,int b,int c,int d){
cnt++;
e[cnt].nt=head[a];
head[a]=cnt;
e[cnt].from=a;
e[cnt].to=b;
e[cnt].cap=c;
e[cnt].flow=0;
e[cnt].cost=d;
cnt++;
e[cnt].nt=head[b];
head[b]=cnt;
e[cnt].from=b;
e[cnt].to=a;
e[cnt].cap=0;
e[cnt].flow=0;
e[cnt].cost=-d;
}
bool spfa(){
memset(mark,0,sizeof mark);
memset(d,inf,sizeof d);
queue<int>q;
q.push(s);
d[s]=0;
mark[s]=1;
while(!q.empty()){
int x=q.front();
q.pop();
mark[x]=0;
for(int i=head[x];i!=-1;i=e[i].nt){
int v=e[i].to;
if(e[i].cap>e[i].flow&&d[v]>d[x]+e[i].cost){
d[v]=d[x]+e[i].cost;
pre[v]=i;
if(!mark[v]){
q.push(v);
mark[v]=1;
}
}
}
}
return d[t]!=inf;
}
void ek(){
mincost=maxflow=0;
while(spfa()){
int k=inf;
for(int i=t;i!=s;i=e[pre[i]].from){
k=min(k,e[pre[i]].cap-e[pre[i]].flow);
}
for(int i=t;i!=s;i=e[pre[i]].from){
e[pre[i]].flow+=k;
e[pre[i]^1].flow-=k;
}
maxflow+=k;
mincost+=k*d[t];
}
}
int main(){
while(scanf("%d%d%d",&n,&m,&k)!=EOF){
if(n+m+k==0)break;
init();
int sum=0;//需要的货物总数
for(int i=1;i<=n;i++){//人
for(int j=1;j<=k;j++){//货物
scanf("%d",&need[i][j]);//数量
sum+=need[i][j];
}
}
for(int i=1;i<=m;i++){//供应点
for(int j=1;j<=k;j++){//货物
scanf("%d",&have[i][j]);//数量
}
}
for(int c=1;c<=k;c++){//货物
for(int i=1;i<=n;i++){//店主
for(int j=1;j<=m;j++){//供应点
scanf("%d",&kcost[c][i][j]);//成本
}
}
}
int allcost=0;
int allflow=0;
for(int j=1;j<=k;j++){//货物
init();
for(int i=1;i<=n;i++){//人
add(s,i,need[i][j],0);//源点到人
}
for(int i=1;i<=n;i++){//人到供应商
for(int c=1;c<=m;c++){
add(i,c+n,need[i][j],kcost[j][i][c]);
}
}
for(int i=1;i<=m;i++){
add(i+n,t,have[i][j],0);
}
ek();
allcost+=mincost;
allflow+=maxflow;
}
if(allflow==sum){//判断是否满足所有人
printf("%d\n",allcost);
}else{
printf("-1\n");
}
}
return 0;
}