POJ - 2516 Minimum Cost 费用流,拆图

题目链接

https://vjudge.net/problem/POJ-2516

题意

n个店铺,从m个市场进货,k种货物。给定市场k种货物存量,店铺k种货物需求,运输花费,问是否能满足要求,最小花费是多少

思路

费用流没得跑,就看如何建图

我一开始是一次性建图,出现以下几个问题

  • 点编号太难了,一一对应很费脑子
  • 边太多了,MCMF超时

看别的博客知道了要拆成k张图,对k种货物一一处理。因为我们需要满足所有需求,也就是必须满流,所以可以独立得拆成k张图,拆了之后发现建图简单了一个量级,按部就班建图就可以了

数据

来源POJ讨论区,感谢前辈们提供数据

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

2 4 3
0 2 3
2 3 4
1 0 2
1 0 3
0 2 4
2 3 0
2 3 1 2
1 2 3 1
1 1 2 3
2 1 3 1
2 2 3 1
2 3 4 1

2 2 1
1 1 1 1
1 2 2 100

2 4 5
2 1 2 2 2
1 1 1 2 1
2 2 2 2 2
2 1 2 1 1
2 2 2 2 1
1 2 2 2 1
1 1 6 7
5 5 2 9
4 6 6 9
9 1 9 1
7 4 1 7
4 4 7 3
5 4 7 7
9 1 3 8
8 8 2 7
1 4 7 1

1 2 3
1 1 2
2 2 1
2 1 1
7 4
4 1
7 3
0 0 0

output:

4
-1
27
4
38
15

代码

#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
using namespace std;
	typedef long long ll;
	const int maxn=55*55;
	const int maxe=500005;
	const int inf=0x3f3f3f3f;
	int n,m,k,s,t,cnt;
	int head[maxn];
	struct Edge{
    
    
		int v,w,next,flow;
	}edge[maxe];
	
	int dis[maxn],incf[maxn],pre[maxn];//incf最小流量 
	bool inque[maxn];
	int maxflow,mincost;
	
	//费用流模版 
	void init(){
    
    
		memset(head,-1,sizeof(head));
		maxflow=mincost=0;
		cnt=0;
	}
	void add(int u,int v,int flow,int cost){
    
    
		//cout<<u<<" "<<v<<" "<<flow<<" "<<cost<<endl;
		edge[cnt].w=cost;
		edge[cnt].v=v;
		edge[cnt].flow=flow;
		edge[cnt].next=head[u];
		head[u]=cnt++;
		return ;
	}
	bool spfa(){
    
    
		queue<int>q;
		memset(dis,0x3f,sizeof(dis));
		memset(inque,0,sizeof(inque));
		q.push(s); dis[s]=0; inque[s]=1; incf[s]=inf;
		while(q.size()){
    
    
			int u=q.front();q.pop();
			inque[u]=0;
			for(int i=head[u];~i;i=edge[i].next){
    
    
				if(!edge[i].flow)	continue;
				int v=edge[i].v;
				if(dis[v]>dis[u]+edge[i].w){
    
    
					dis[v]=dis[u]+edge[i].w;
					incf[v]=min(incf[u],edge[i].flow);
					pre[v]=i;
					if(!inque[v]){
    
    
						q.push(v);
						inque[v]=1;
					}
				}
			}
		}
		if(dis[t]==inf) return 0;
		return 1;
	}
	void MCMF(){
    
    
		while(spfa()){
    
    
			int x=t;
			maxflow+=incf[t];
			mincost+=dis[t]*incf[t];
			int i;
			while(x!=s){
    
    
				i=pre[x];
				edge[i].flow-=incf[t];
				edge[i^1].flow+=incf[t];
				x=edge[i^1].v;
			}
		}
	}
	
	int dian[55][55];//dian i j       i店j货物所需 
	int ku[55][55];//ku i j           i库j货物拥有 
	int val[55][55][55];//val w i j   w货物从j库到i店所需 
	signed main(){
    
    
		while(scanf("%d%d%d",&n,&m,&k)==3){
    
    
			int ans=0;
			if(!n&&!m&&!k)
				break;
			for(int i=1;i<=n;i++){
    
    
				for(int j=1;j<=k;j++){
    
    
					scanf("%d",&dian[i][j]);
				}
			}
			for(int i=1;i<=m;i++){
    
    
				for(int j=1;j<=k;j++)
					scanf("%d",&ku[i][j]);
			}
			for(int w=1;w<=k;w++){
    
    
				for(int i=1;i<=n;i++)
					for(int j=1;j<=m;j++)
						scanf("%d",&val[w][i][j]);
			}
			for(int _=1;_<=k;_++){
    
    
				int need=0;
				init();
				s=n*m+1000,t=s+1;
				//向汇点连边 
				for(int i=1;i<=n;i++){
    
    
					need+=dian[i][_];
					add(i,t,dian[i][_],0);
					add(t,i,0,0);
				}
				//源点连边 
				for(int i=1;i<=m;i++){
    
    
					add(s,i+n,ku[i][_],0);
					add(i+n,s,0,0);
				}
				//市场-店面 
				for(int i=1;i<=n;i++){
    
    
					for(int j=1;j<=m;j++){
    
    
						add(n+j,i,ku[j][_],val[_][i][j]);
						add(i,n+j,0,-val[_][i][j]);
					}
				}
				MCMF();
				if(maxflow!=need){
    
    //满足不了 
					ans=-1;
					break;
				}
				else
					ans+=mincost;
			}
			printf("%d\n",ans);
		}
	}

猜你喜欢

转载自blog.csdn.net/TheSunspot/article/details/108266220
今日推荐