POJ2195,Going Home(二分图带权匹配)

本人是用最小费用最大流变形过的,关于最小费用最大流可参考博客:
https://blog.csdn.net/LeYOUNGER/article/details/70156905 (算法证明)
https://blog.csdn.net/y990041769/article/details/40074527 (模板)
150+MS过,在讨论中看到有人用KM算法(一种处理关于二分图在完备匹配下的最大带权匹配的算法),粘贴代码之后交了,0MS过,说明KM算法处理这类问题时还是有优势的,日后找个时间学学。

代码如下:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn=2e2+10;
const int maxm=1e5+10;
const int inf=0x3f3f3f3f;
int map[maxn][maxn],head[maxn],cnt;
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}},N,M,num;
struct edge
{
	int from,to,cap,cost,next;
}e[maxm];
struct block
{
	int id,x,y,mov;
	block(int id,int x,int y,int mov){ this->id=id; this->x=x; this->y=y; this->mov=mov; };
};
struct MCMF
{
	int s,t,flow,cost;
	int dis[maxn],pre[maxn],a[maxn];
	bool inq[maxn];
	
	bool Bellman_Ford()
	{
		for(int i=0;i<=t;i++)	dis[i]=inf;
		mem(inq,0);
		dis[s]=0; inq[s]=1; a[s]=inf; pre[s]=0; 
		queue<int> Q;
		Q.push(s);
		while(!Q.empty())
		{
			int u=Q.front(); Q.pop();
			inq[u]=0;
			for(int i=head[u]; i ; i=e[i].next)
			{
				if(e[i].cap&&dis[e[i].to]>dis[u]+e[i].cost)
				{
					dis[e[i].to]=dis[u]+e[i].cost;
					pre[e[i].to]=i;
					a[e[i].to]=min(a[u],e[i].cap);
					if(!inq[e[i].to])
					{
						Q.push(e[i].to);
						inq[e[i].to]=1;
					}
				}
			}
		}
		if(dis[t]==inf)	return 0;
		flow+=a[t];
		cost+=dis[t]*a[t];
		int u=t;
		while(u!=s)
		{
			e[pre[u]].cap-=a[t];
			e[pre[u]^1].cap+=a[t];
			u=e[pre[u]].from;
		}
		return 1;
	}
	
	void mcmf()
	{
		flow=cost=0;
		while(Bellman_Ford());
		printf("%d\n",cost);
	}
}m;

void addedge(int u,int v,int w,int c)
{
	e[cnt].from=u; e[cnt].to=v; e[cnt].cap=w; e[cnt].cost=c; e[cnt].next=head[u]; head[u]=cnt++;
	e[cnt].from=v; e[cnt].to=u; e[cnt].cap=0; e[cnt].cost=-c; e[cnt].next=head[v]; head[v]=cnt++;
}

void BFS(int id,int x,int y)
{
	queue<block> Q;
	int vis[maxn][maxn];
	Q.push( block(id,x,y,0) );
	for(int i=1;i<=N;i++)	for(int j=1;j<=M;j++)	vis[i][j]=0;
	vis[x][y]=1;
	while(!Q.empty())
	{
		block fro=Q.front(); Q.pop();
		if(fro.id>num)
		{
			addedge(id,fro.id,1,fro.mov);
		}
		for(int i=0;i<4;i++)
		{
			int x0=fro.x+dir[i][0],y0=fro.y+dir[i][1];
			if(x0>=1&&x0<=N&&y0>=1&&y0<=M)
			{
				if(vis[x0][y0])	continue;
				Q.push( block(map[x0][y0],x0,y0,fro.mov+1) );
				vis[x0][y0]=1;
			}
		}
	}
}

void init()
{
	mem(head,0);
	cnt=2;
	for(int i=1;i<=num;i++)
		addedge(0,i,1,0);
	for(int i=num+1;i<=2*num;i++)
		addedge(i,2*num+1,1,0);
	m.s=0; m.t=2*num+1;
	for(int i=1;i<=N;i++)
		for(int j=1;j<=M;j++)
			if(map[i][j]&&map[i][j]<=num)
				BFS(map[i][j],i,j);
}

int main()
{
	while(~scanf("%d%d",&N,&M))
	{
		if(!N&&!M)	break;
		num=0;
		int k1,k2;
		for(int i=1;i<=N;i++)
		{
			char s[maxn];
			scanf("%s",s);
			for(int j=0;j<M;j++)
			{
				if(s[j]=='H')
					map[i][j+1]=2;
				else if(s[j]=='m')
				{
					map[i][j+1]=1;
					num++;
				}
				else map[i][j+1]=0;
			}
		}
		k1=1; k2=num+1;
		for(int i=1;i<=N;i++)
			for(int j=1;j<=M;j++)
				if(map[i][j]==2)
					map[i][j]=k2++;
				else if(map[i][j]==1)
					map[i][j]=k1++;
		init();
		m.mcmf();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值