HDU 3468 BFS + 二分匹配

九野的博客,转载请注明出处 http://blog.csdn.net/acmmmm/article/details/10966383

 

题意:给定n,m表示下面地图大小

.表示空地 #表示墙 *表示黄金

行走的路线是A->Z || a->z

规则,必须从字母依次走最短路到下一个字母(字母必须连续走,如果走不到下一个字母或者地图上不存在下一个字母则输出-1)

每次走到下一个字母可以取走路途上的一个黄金,问最多能取走几个黄金

 

思路:走到最后一个字母就结束了,所以希望从每个字母走出来都能得到一个黄金,所以是二分匹配

把起点字母作为X集, 黄金作为Y集, 映射条件是黄金在 字母间行走的最短路上

若黄金在最短路上 则满足: 起点到终点距离 = 黄金到起点距离 + 黄金到终点距离

 

然后这里用BFS寻找路径建图

最后套个二分匹配的模版

 

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<set>
#include<math.h>
#include<string.h>
#include<queue>
#include<vector>
#define N 105
#define inf 999999999
using namespace std;
int n,M;
int road[N],p[N*N],gold[N*N],num,pn;//road 表示字母在p中的编号,p是字母的坐标(一维化)
int dis[N][N*N];//dis[a][b] 表示离散化的字母点a到 一维化的坐标b的距离
char map[N][N];
vector<int>G[N];

int go[4][2]={1,0,0,1,-1,0,0,-1};
void bfs(int s){
	bool vis[N*N];
	memset(vis,0,sizeof(vis));
	queue<int>q;

	memset(dis[s],-1,sizeof(dis[s]));
	dis[s][p[s]]=0;

	q.push(p[s]);
	vis[p[s]]=1;
	while(!q.empty())
	{
		int t=q.front();
		int x=t/M,y=t%M;
		q.pop() ;
		for(int i=0;i<4;i++)
		{
			int nowx=x+go[i][0],nowy=y+go[i][1];
			int now=nowx*M+nowy;
			if(map[nowx][nowy]=='#')continue;
			if(nowx<0 || nowx>=n ||nowy<0||nowy>=M)continue;
			if(vis[now])continue;
			vis[now]=1;

			dis[s][now]=dis[s][t]+1;
			q.push(now);
		}
	}
}
int lef[N*N];//lef[v]表示右边点v 当前连接的点
bool T[N*N];//右边的点是否连过

bool match(int x)
{
	for(int i=0;i<G[x].size();i++)
	{
		int v=G[x][i];
		if(!T[v])
		{
			T[v]=true;
			if(lef[v]==-1||match(lef[v]))
			{
				lef[v]=x;
				return true;
			}
		}
	}
	return false;
}

void solve()
{
	int ans=0;
	memset(lef,-1,sizeof(lef));
	for(int i=0;i<pn-1;i++)//左右点集匹配,左点集是 0-(pn-1) 右点集是G[左点].size()
	{
		memset(T,0,sizeof(T));

		if(match(i))ans++;
	}
	printf("%d\n",ans);
}

int f(char c){
	if('A'<=c && c<='Z')return c-'A';
	else if('a'<=c && c<='z')return c-'a'+26;
}
int main(){
	int i,j;
	while(~scanf("%d%d",&n,&M)){
		pn=num=0;
		memset(road,-1,sizeof(road));//这句没有最后第二个案例过不了
		for(i=0;i<n;i++)
		{
			scanf("%s",map[i]);
			for(j=0;j<M;j++)
				if(isalpha(map[i][j]))
				{
					p[pn]=i*M+j;
					road[f(map[i][j])]=pn;
					pn++;
				}
				else if(map[i][j]=='*')
				{
					gold[num++]=i*M+j;
	 			}
		}
		
		for(i=0;i<pn;i++)bfs(i),G[i].clear();
		for(i=0;i<pn-1;i++)
		{
			if(road[i]==-1||road[i+1]==-1)	        break;
			if(dis[road[i]][p[road[i+1]]]==-1) 	break;
		}
		if(i!=pn-1){puts("-1");continue;}
		for(i=0;i<pn-1;i++)
			for(j=0;j<num;j++)
			{
				if(dis[road[i]][gold[j]]==-1 || dis[road[i+1]][gold[j]]==-1)continue;//j这点的黄金到不了字母点
				if(dis[road[i]][gold[j]]+dis[road[i+1]][gold[j]]==dis[road[i]][p[road[i+1]]])
					G[i].push_back(j);
			}
		solve();
	}
	return 0;
}
/*
3 4
A#B.
***C
.D..

4 4
A#B.
***C
.D..
..E*

4 4
A#B.
***C
.D..
.*E*

4 4
A#B.
***C
.D#.
.#E*

4 4
A#B.
***C
.D#.
.#E.

4 4
A#B.
***C
.D##
.#E.

4 4
A#B.
***C
.D#*
.#E.

4 4
a#b.
***c
.d#*
.#e.

4 4
A#B*
*.*C
..D#
E*..


ans;
3
3
4
4
3
-1
4
-1
4

*/


 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值