(bfs)连连看(hdu1175),HDU 1253 胜利大逃亡(三维数组+bfs),HDU 1495 非常可乐(BFS+模拟)

这篇文章介绍了如何利用BFS(广度优先搜索)算法解决HDOJ 1175连连看问题,着重于处理棋盘中通过最少转弯次数连接相同棋子的判断。通过维护一个标记数组来跟踪从起点到每个位置的转弯次数,确保找到从起点到终点的最短路径不超过两次转弯。
摘要由CSDN通过智能技术生成

连连看(hdu1175)

“连连看”相信很多人都玩过。没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子。如果某两个相同的棋子,可以通过一条线连起来(这条线不能经过其它棋子),而且线的转折次数不超过两次,那么这两个棋子就可以在棋盘上消去。不好意思,由于我以前没有玩过连连看,咨询了同学的意见,连线不能从外面绕过去的,但事实上这是错的。现在已经酿成大祸,就只能将错就错了,连线不能从外围绕过。
玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序。
Input
输入数据有多组。每组数据的第一行有两个正整数n,m(0<n<=1000,0<m<1000),分别表示棋盘的行数与列数。在接下来的n行中,每行有m个非负整数描述棋盘的方格分布。0表示这个位置没有棋子,正整数表示棋子的类型。接下来的一行是一个正整数q(0<q<50),表示下面有q次询问。在接下来的q行里,每行有四个正整数x1,y1,x2,y2,表示询问第x1行y1列的棋子与第x2行y2列的棋子能不能消去。n=0,m=0时,输入结束。
注意:询问之间无先后关系,都是针对当前状态的!
Output
每一组输入数据对应一行输出。如果能消去则输出"YES",不能则输出"NO"。
Sample Input
3 4
1 2 3 4
0 0 0 0
4 3 2 1
4
1 1 3 4
1 1 2 4
1 1 3 3
2 1 2 4
3 4
0 1 4 3
0 2 4 1
0 0 0 0
2
1 1 2 4
1 3 2 3
0 0
Sample Output
YES
NO
NO
NO
NO
YES
思路:
这个题需要考虑转向,转向的话就是根据到当前数走的方向与到上个数走的方向是否相同来判断。如果不同,,则一定是转弯,转弯的数要加一。现在我们有坐标,方向转弯次数就好写了,其中我们认为方向0就是向下,1向右,2向上,3向左,-1是最初的x1y1的方向(也就是第一次出发时,没有选择方向这也算一次,但是在题中不算方向,所以从-1开始)。
由于他是询问x1y1到x2y2,我们只需判断是否有x1y1到x2y2的路,这个题让我刷新了对标记数组的认识,之前的标记数组都是经过了就标记一下,下次在经过这个坐标时如果已做过标记就不走。但这个是用于记录从(x1,y1)到(i,j)转弯的最小次数,如果在(i,j)这个位置的wan<=v[i][j],就push到队列里,我试了一下,如果把这个标志数组去掉就会超时,就是从起点到一个点的的路径有多条,但是如果只是说经过了就不再考虑到这个点的其他路径的话,这个点到终点的转弯次数加上起点到这个点的转弯次数会超过二,这是不满足题意的,所以我们到这个点之后还需要考虑其他到这个点的路径,其唯一目的就是让从起点到终点的路径转弯的次数最小,这样的话,就有极大的可能从这个路径一直走到终点的过程中转弯的次数满足题意,所以我们标记数组只存最小或者等于目前最小的转弯次数的路径 ,如果step比2小,就继续搜索.
AC代码(注释可以看看):

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std; 
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int n,m,q,x1,y1,x2,y2,flag;
const int manx=1005;
//地图数组合标记数组
int a[manx][manx],vis[manx][manx];
int f[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
struct node{
//dir记录方向,wan记录转弯次数
	int x,y,wan,dir;
}; 
node now,nex;
void bfs(int x,int y){
	memset(vis,10,sizeof(vis));//标志数组 初始化相对较大记录某条路径在该点转向的次数
	queue <node> q;
	now.x=x;
	now.y=y;
	now.dir=-1;-1表示第一次没有选方向,在出发点
	now.wan=0;
	q.push(now);
	while(!q.empty()){
		now=q.front();
		q.pop();
		if(now.x==x2&&now.y==y2&&now.wan<=2){
			flag=1;
			return ;
		}
		for(int i=0;i<4;i++){
			nex=now;
			nex.x=now.x+f[i][0];
			nex.y=now.y+f[i][1];
			if(nex.x>0&&nex.x<=n&&nex.y>0&&nex.y<=m&&(a[nex.x][nex.y]==0||
			(nex.x==x2&&nex.y==y2))){
				if(nex.dir!=-1){//是否是第一次选方向
					if(nex.dir!=i){//nex.dir是上上个坐标走到上个坐标的方向,这个坐标要从新记录 
						nex.dir=i;
						nex.wan++;
					}
				}
				else{//是第一次选方向
					nex.dir=i;
				}
				if(nex.wan>2) continue;
				//保证转弯次数最少,等号不能忘记,
				//(现在转弯次数,如果比以前少则加入队列,)
				if(nex.wan<=vis[nex.x][nex.y]){
					vis[nex.x][nex.y]=nex.wan;
					q.push(nex);
				}
			}
		}
	}
	return ;
}
int main(int argc, char** argv) {
	while(scanf("%d %d",&n,&m)&&n&&m){
		memset(a,0,sizeof(a));
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				scanf("%d",&a[i][j]);
			}
		}
		scanf("%d",&q);
		for(int i=1;i<=q;i++){
			scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
			flag=0;
			//坐标相同,或者点数不同,或者是通道0没有点数 
			if(a[x1][y1]!=a[x2][y2]||a[x1][y1]==0||a[x2][y2]==0||(x1==x2&&y1==y2)){
				flag=0;
			}
			//printf("+++\n");
			else bfs(x1,y1);
			if(flag){
				printf("YES\n");
			}else{
				printf("NO\n");
			}
		}
	}
	return 0;
}

这道题有个图不好复制,就先放个地址O(∩_∩)O哈哈~:
http://acm.hdu.edu.cn/showproblem.PHP?pid=1253

HDU 1253 胜利大逃亡(三维数组+bfs)

这个其实就是个模板题,只是把换成三维数组在加一个时间限制:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn = 55;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int a[maxn][maxn][maxn],v[maxn][maxn][maxn];
int zou[6][3]={{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}};
int A,B,C,T,k;
struct node{
	int x,y,z,step;
};  
int bfs(){
	queue<node>q;
	node now,nex;
	now.x=0;
	now.y=0;
	now.z=0;
	now.step=0;
	v[0][0][0]=1;
	q.push(now);
	while(!q.empty()){
		now=q.front();
		q.pop();
		if (now.step>T)   
        {  
            break;  
        } 
		if(now.x+1==A&&now.y+1==B&&now.z+1==C&&now.step<=T){
			return now.step;
		}
		for(int i=0;i<6;i++){
			nex.x=now.x+zou[i][0];
			nex.y=now.y+zou[i][1];
			nex.z=now.z+zou[i][2];
			nex.step=now.step;
			if(nex.x>=0&&nex.x<A&&nex.y>=0&&nex.y<B&&nex.z>=0&&nex.z<C&&
			v[nex.x][nex.y][nex.z]==0&&a[nex.x][nex.y][nex.z]==0){
				nex.step++;
				v[nex.x][nex.y][nex.z]=1;
				q.push(nex);
				
			}
		}
	}
	return -1;
}
int main(int argc, char** argv) {
	 scanf ("%d",&k);
	while(k--){
	 	scanf ("%d%d%d%d",&A,&B,&C,&T); 
		for(int i=0;i<A;i++){
			for(int j=0;j<B;j++){
				for(int k=0;k<C;k++){
					scanf ("%d",&a[i][j][k]); 
				}
			}
		}
		memset(v,0,sizeof(v));
		cout<<bfs()<<endl;
	}
	return 0;
}

HDU 1495 非常可乐(BFS+模拟)
这次好像是做了一两次模拟吧,感觉好久没做,有点楞,但还好这道题不难
思路和分析;
开始的时候,所有可乐在s中,2个杯子n和m都空着。过程中,可以将任何1个容器中的可乐倒到另外某个容器中,或将目标容器倒满,或将源容器倒空。因为容器没有刻度,只能这样。这个过程中,如果出现某个容器空,另外2个容器可乐相同则成功。如果开始的时候,可乐的容量是奇数,则不可能平分可乐。容器间可乐倒来倒去,每次有6种倒法,对这6种倒法进行试探即可。求的是最少倒的次数,所以可以用BFS实现。
注意一下:重点是倒满和倒空

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std; 
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
const int maxn=105;
bool tai[maxn][maxn][maxn];
struct node{
	int s,n,m,step;
};
int S,M,N;
int bfs(){
	queue<node>q;
	node now,nex;
	//如果实际数是不可能平分的
	if(S%2==1){
		return -1;
	}
	memset(tai,true,sizeof(tai));
	//两杯平分的量
	int s1=S/2;
	now.s=S;
	now.n=0;
	now.m=0;
	now.step=0;
	tai[now.s][now.n][now.m]=false;
	q.push(now);
	while(!q.empty()){
		now=q.front();
		q.pop();
		//平分的情况
		if(((now.s==now.n)&&(now.s==s1))||((now.s==now.m)&&(now.s==s1))||
		((now.m==now.n)&&(now.m==s1))){
			return now.step;
		}
		//s-n
		if(now.s&&N-now.n>0){
			if(now.s>N-now.n){ // s > n的剩余容量
				nex.s=now.s-(N-now.n);
				nex.n=N;
				nex.m=now.m;
			}else{// s <= m的剩余容量
				nex.s=0;
				nex.n=now.n+now.s;
				nex.m=now.m;
			}
			if(tai[nex.s][nex.n][nex.m]){
				nex.step=now.step+1;
				tai[nex.s][nex.n][nex.m]=false;
				q.push(nex);
			}
		}
		//s-m
		if(now.s&&M-now.m>0){
			if(now.s>M-now.m){
				nex.s=now.s-(M-now.m);
				nex.n=now.n;
				nex.m=M;
			}else{
				nex.s=0;
				nex.n=now.n;
				nex.m=now.m+now.s;
			}
			if(tai[nex.s][nex.n][nex.m]){
				nex.step=now.step+1;
				tai[nex.s][nex.n][nex.m]=false;
				q.push(nex);
			}
		}
		//n-m
		if(now.n&&M-now.m>0){
			if(now.n>M-now.m){
				nex.m=M;
				nex.n=now.n-(M-now.m);
				nex.s=now.s;
			}else{
				nex.n=0;
				nex.s=now.s;
				nex.m=now.m+now.n;
			}
			if(tai[nex.s][nex.n][nex.m]){
				nex.step=now.step+1;
				tai[nex.s][nex.n][nex.m]=false;
				q.push(nex);
			}
		}
		//n-s
		if(now.n&&S-now.s>0){
			if(now.n>S-now.s){
				nex.m=now.m;
				nex.n=now.n-(S-now.s);
				nex.s=S;
			}else{
				nex.n=0;
				nex.s=now.s+now.n;
				nex.m=now.m;
			}
			if(tai[nex.s][nex.n][nex.m]){
				nex.step=now.step+1;
				tai[nex.s][nex.n][nex.m]=false;
				q.push(nex);
			}
		}
		//m-s
		if(now.m&&S-now.s>0){
			if(now.m>S-now.s){
				nex.m=now.m-(S-now.s);
				nex.n=now.n;
				nex.s=S;
			}else{
				nex.m=0;
				nex.s=now.s+now.m;
				nex.n=now.n;
			}
			if(tai[nex.s][nex.n][nex.m]){
				nex.step=now.step+1;
				tai[nex.s][nex.n][nex.m]=false;
				q.push(nex);
			}
		}
		//m-n
		if(now.m&&N-now.n>0){
			if(now.m>N-now.n){
				nex.m=now.m-(N-now.n);
				nex.n=N;
				nex.s=now.s;
			}else{
				nex.m=0;
				nex.n=now.n+now.m;
				nex.s=now.s;
			}
			if(tai[nex.s][nex.n][nex.m]){
				nex.step=now.step+1;
				tai[nex.s][nex.n][nex.m]=false;
				q.push(nex);
			}
		}
	}
	return -1;
}
int main(int argc, char** argv) {
	while(scanf("%d %d %d",&S,&N,&M)&&S&&M&&N){
		int ans=bfs();
		if(ans>0){
			printf("%d\n",ans);
		}else{
			printf("NO\n");
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值