POJ - 2195 Going Home 费用流+BFS

题目链接

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

题意

给出n*m网格,中间有多个小人和房子,小人走一步花1¥,要求所有小人都到家(人房无对应关系),求最小花费

思路

第一眼以为是最短路,但由于小人和房子无对应关系,也就是一个小人选择不同房子会影响别的小人,所以直接最短路肯定wa的。

因为每个小人都要回一个房子,在这个基础上求最小花费。所以可以套费用流,超级源点向小人连边权1,房子向汇点连边权1,那么再来建立小人-房子的关系,费用流跑出来是最大流条件下(人人有房住)下最小花费,就是答案了。

具体小人-房子的联系,就遍历全图,对每一个小人跑bfs就行了。

代码

#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=205;
	const int maxe=30005;
	const int inf=0x3f3f3f3f;
	int n,m,s,t,cnt;
	int gh[maxn][maxn]; 
	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;
			}
		}
	}
	
	//bfs建图 
	int dxy[][2]={
    
    {
    
    1,0},{
    
    -1,0},{
    
    0,1},{
    
    0,-1}};
	bool vis[maxn][maxn];
	int dis_bfs[maxn][maxn];
	
	inline bool ju(int x,int y){
    
    
		if(x<1||x>n||y<1||y>m||vis[x][y])
			return 0;
		return 1;
	}
	
	void find(int x,int y){
    
    
		memset(vis,0,sizeof vis);
		queue<pair<int,int> >q;
		pair<int,int>p;
		q.push(make_pair(x,y));
		dis_bfs[x][y]=0;
		vis[x][y]=1;
		while(q.size()){
    
    
			p=q.front(),q.pop();
			//找到了房子,连边 
			if(gh[p.first][p.second]<0){
    
    
				add(gh[x][y],-gh[p.first][p.second]+100,1,dis_bfs[p.first][p.second]);
				add(-gh[p.first][p.second]+100,gh[x][y],0,-dis_bfs[p.first][p.second]);
			}
			for(int i=0;i<4;i++){
    
    
				int tx=p.first+dxy[i][0],ty=p.second+dxy[i][1];
				if(ju(tx,ty)){
    
    
					vis[tx][ty]=1;
					q.push(make_pair(tx,ty));
					dis_bfs[tx][ty]=dis_bfs[p.first][p.second]+1;
				}
			}
		}
	}
	
	int main(){
    
    
		while(scanf("%d%d",&n,&m)==2){
    
    
			if(!n&&!m)
				break;
			s=201,t=202;
			init();
			getchar();
			int cnt1=1,cnt2=-1;//编号 
			for(int i=1;i<=n;i++)
				for(int j=1;j<=m;j++){
    
    
					char ch=getchar();
					while(ch=='\n'||ch==' ')
						ch=getchar();
					if(ch=='.')
						gh[i][j]=0;
					else if(ch=='m')
						gh[i][j]=cnt1++;
					else
						gh[i][j]=cnt2--;
				}
			for(int i=1;i<=n;i++)
				for(int j=1;j<=m;j++)
					if(gh[i][j]>0)
						find(i,j);
			for(int i=1;i<=n;i++)
				for(int j=1;j<=m;j++)
					if(gh[i][j]>0){
    
    //源点-人 
						add(s,gh[i][j],1,0);
						add(gh[i][j],s,0,0);
					}
					else if(gh[i][j]<0){
    
    //房子-汇点 
						add(-gh[i][j]+100,t,1,0);
						add(t,-gh[i][j]+100,0,0);
					}
			MCMF();
			printf("%d\n",mincost);
		}
	}

猜你喜欢

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