HDU 3468 双bfs+二分图

本文介绍了一种寻宝游戏的算法实现,通过两次广度优先搜索(BFS)确定最短路径并找到宝藏位置,利用二分图匹配计算可获取的最大宝藏数量。如果无法达到某一集结点则返回-1。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

寻宝游戏
Time Limit: 1000MS Memory Limit: 65536K
   

Description

你喜欢寻宝游戏吗?今天,小明就要和他的朋友展开一场冒险之旅。他们可能在路上找到许多黄金。
小明的朋友非常聪明,已经绘制出了一张寻宝地图,为了简单化,地图上只有三种标识:
• ‘.’ 表示他们可以通过的空地。
• ‘#’ 表示他们无法通过的区域。
• ‘*’ 表示在该地下,可以找到黄金。。。

令小明高兴的是,他的朋友对不属于他的东西没有任何兴趣,当然包括脚下
的黄金。。。那样所有的黄金就都可以属于小明了。。。
但是他的朋友也有一个要求,他在地图上设置了一系列集结点,取名为'A', 'B' ... 'Z', 'a', 'b' ... 'z'(它们是按照顺序的,并且最多不超过52个)他们从'A'出发。在任意时刻,小明的朋友都会选择一条最短的路,达下一个集结点休息。小明可以选择和他一样的路径,也可以选择其他的,但是,必须同时到达。小明对于宝藏的渴望,使得他浑身充满了力量,在到达下一个集结点前,可以在路上选择一个有黄金的地点,几乎不需时间就可以把它挖出来,当然这股力量不会积累,即每次休息过后至多只能挖出一个黄金。挖出黄金的地点自动变成‘.’,不可能再长出黄金来。
那么小明想要知道,在服从朋友的要求下,他最多能挖出多少个宝藏。

Input

第一行两个整数R和C。(2<=R,C<=100)表示地图的行和列数。
接下来R行,每行C个字符,表示地图的状态。仅包含‘A’ ? ‘Z’ or ‘a’ ? ‘z’ or ‘.’ or ‘#’ or ‘*’。

Output

一个整数,表示小明能获得的最多宝藏数,如果有一个集结点无法到达,则输出-1。

Sample Input

2 4
A.B.
***C
2 4
A#B.
***C

Sample Output

1
2



思路:模拟从A走到B,B走到C....的过程,对于每一次到下一个目标点,我们先bfs预处理当前起点到地图所有点的距离,然后从当前要去的终点bfs搜回来,如果搜到某个点在最短路上,那就继续扩展,否则就放弃这个点,如果这个点同时满足最短路点和宝藏,那么就把这个点和当前是第几次去下一个目标点建一条边,为了方便起见,我们把所有距离都定为原距离+1,这样就不会重复走了,搜索完之后,我们会得到一个图,信息就是第几次出发能挖到的宝藏,这时候我们再进行二分图匹配就可以了。注意bfs过程中,如果发现下一个点到不了,那就break掉,输出-1。


#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<deque>
using namespace std;
struct node{
	int x,y;
}sss[60];
deque<node>sp;
char map[105][105],ss[55];
int n,m,sx,sy,ex,ey,viss[105][105],sea[60][10005],visss[105][105],match[10005],vis[10005],num;
int dirx[4]={0,0,1,-1},diry[4]={1,-1,0,0};
bool dfs(int u){//二分图匹配
	for(int v=1;v<=n*m;v++){
		if(sea[u][v]&&!vis[v]){
			vis[v]=1;
			if(match[v]==-1||dfs(match[v])){
				match[v]=u;
				return true;
			}
		}
	}
	return false;
}
int main(){
	int i,j;
	for(i=1;i<=26;i++)ss[i]=i+'A'-1;
	for(i=27;i<=52;i++)ss[i]=i-27+'a';//每个目标点
	while(scanf("%d%d",&n,&m)!=EOF){
		char c='A';
		int flag=1;
		memset(sea,0,sizeof(sea));
		for(i=1;i<=n;i++){
			scanf("%s",map[i]+1);
			for(j=1;j<=m;j++){
				if(map[i][j]=='A'){//起点
					sx=i;
					sy=j;
				}
				if(map[i][j]!='*'&&map[i][j]!='#'&&map[i][j]!='.'&&map[i][j]>c){
					c=map[i][j];//终点的字母
				}
				if(map[i][j]>='B'&&map[i][j]<='Z'){//每个目标点的位置
					sss[map[i][j]-'A'+1].x=i;
					sss[map[i][j]-'A'+1].y=j;
				}
				if(map[i][j]>='a'&&map[i][j]<='z'){
					sss[map[i][j]-'a'+27].x=i;
					sss[map[i][j]-'a'+27].y=j;
				}
			}
		}
		if(c>='a')num=c-'a'+27;
		else num=c-'A'+1;//有多少目标点
		for(i=1;i<num;i++){
			memset(viss,0,sizeof(viss));
			memset(visss,0,sizeof(visss));
			int dis=0;
			sp.clear();
			sp.push_back((node){sx,sy});
			viss[sx][sy]=1;
			while(!sp.empty()){//第一次bfs,找出距离
				node f=sp.front();
				sp.pop_front();
				int x=f.x;
				int y=f.y;
				if(map[x][y]==ss[i+1]){//找到这次bfs的终点
					dis=viss[x][y]+1;//距离定为+1,假设起点为1,终点为10,那么第二次bfs起点为1,终点为10,10+1=11,所以是dis=距离+1
					sx=x;
					sy=y;
				}
				for(j=0;j<4;j++){
					int px=x+dirx[j],py=y+diry[j];
					if(px>=1&&px<=n&&py>=1&&py<=m&&map[px][py]!='#'&&!viss[px][py]){
						viss[px][py]=viss[x][y]+1;
						sp.push_back((node){px,py});
					}
				}
			}
			if(viss[sss[i+1].x][sss[i+1].y]==0){//如果不能到达下一个目标点
				flag=0;
				break;
			}
			sp.push_back((node){sx,sy});
			visss[sx][sy]=1;
			while(!sp.empty()){//第二次bfs
				node f=sp.front();
				sp.pop_front();
				int x=f.x;
				int y=f.y;
				if(visss[x][y]+viss[x][y]>dis)continue;//如果当前点已经不是最短路的点,那就continue掉
				if(map[x][y]=='*'){//如果当前是最短路点且当前点是宝藏,那么就建一条边
					sea[i][(x-1)*m+y]=1;
				}
				for(j=0;j<4;j++){
					int px=x+dirx[j],py=y+diry[j];
					if(px>=1&&px<=n&&py>=1&&py<=m&&map[px][py]!='#'&&!visss[px][py]){
						visss[px][py]=visss[x][y]+1;
						sp.push_back((node){px,py});
					}
				}
			}
		}
		if(!flag){//如果无法到达某一个集结点
			printf("-1\n");
			continue;
		}
		int sum=0;
		memset(match,-1,sizeof(match));
		for(j=1;j<num;j++){//二分图匹配
			memset(vis,0,sizeof(vis));
			if(dfs(j))sum++;
		}
		printf("%d\n",sum);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值