http://www.cppblog.com/mythit/archive/2009/04/29/81497.html?opt=admin
玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序。
注意:询问之间无先后关系,都是针对当前状态的!
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
YES NO NO NO NO YES
一看题目就知道是用bfs,但是要注意几个问题:bfs是按层次进行搜索,得到的从起点到终点的路径(如果存在的话)是最短的。题目中说这条路径最多只能转向2次,有一些情况可能得到了从起点到终点的路径,但是它的转向次数已经超过的2次,这样这条路径就不符合要求,得重新找一条。一个一般的结论:如果某一点记录的转向次数大于当前路径在该点的转向次数,那么还能从该点再发出一条路径来查找。可以用一个二维数组hash[n][n]来状态判重,这个数组里存的数值就是某条路径在该点的转向次数,if(hash[x][y]>=now.turn) q.push(now);还有个需要注意的就是能连上的点并没有从图中消失,所以每条查询语句都是独立的。(我把它当成真正的连连看来做,结果WA了10次)
#include <iostream>
#include <queue>
#include<cstdio>
using namespace std;
const int N = 1001;
bool flag;
int n,m,sx,sy,ex,ey;
int hash[N][N],map[N][N];
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
struct node{
int x,y,turn,d;
}start;
queue<node> q;
inline bool in(const node &p){
if(p.x<0 || p.y<0 || p.x>=n || p.y>=m)
return false;
return true;
}
void bfs(){
node now,t;
while(!q.empty()){
now=q.front(),q.pop();
if(now.x==ex && now.y==ey && now.turn<=2)
{
flag=true;
return;
}
for(int i=0;i<4;i++)
{
if(i==(now.d+2)%4) continue ;//不能往回走
t.x=now.x+dir[i][0],t.y=now.y+dir[i][1];
if(now.d==i)
t.turn=now.turn,t.d=now.d;
else
t.turn=now.turn+1,t.d=i;
if(in(t) && (map[t.x][t.y]==0||(t.x==ex&&t.y==ey)) && hash[t.x][t.y]>=t.turn&&t.turn<=2)
hash[t.x][t.y]=t.turn,q.push(t);
}
}
}
int main()
{
int i,j,t;
while(scanf("%d %d",&n,&m),n||m)
{
for(i=0;i<n;i++)
for(j=0;j<m;j++)
{
scanf("%d",&map[i][j]);
}
scanf("%d",&t);
while(t--)
{
scanf("%d %d %d %d",&sx,&sy,&ex,&ey);
sx--,sy--,ex--,ey--;
if((map[sx][sy]!=map[ex][ey]) || map[sx][sy]==0 || map[ex][ey]==0 || (sx==ex&&sy==ey))
{
puts("NO");
continue;
}
for(i=0;i<n;i++)
for(j=0;j<m;j++)
hash[i][j]=111;
while(!q.empty()) q.pop();
for(i=0;i<4;i++)
{
start.x=sx,start.y=sy,start.turn=0,start.d=i;
q.push(start);
}
flag=false;
//设置为0,防止绕圈
hash[sx][sy]=0;
bfs();
puts(flag ? "YES":"NO");
}
}
return 0;
}
一般的bfs是用来求取最短距离的,于是定好起始点,按照四个方向搜索就可以了,每个点只是也只是允许访问一次,但是在这个连连看当中,是可以允许重复访问点的,因为我们要的不是距离最短的点,而是转弯最少的线路,于是我们增加了一个变量用于存储转弯的次数,当这个点的转弯的次数变得更少的时候我们就更新这个点到队列当中去,并更新这个点的turn。于是会走很多冤枉路,耗时很长
http://blog.chinaunix.net/uid-26602509-id-3179280.html
4. 连连看
这其实是《编程之美》第一章”1.14 连连看游戏设计“中介绍的一个算法,连连看可以由一个二维数组表示,数组元素的值表示不同的图形,我们可以用0表示该位置没有图形。 连连看中两个结点能够相连从而消去的标准是:相连不超过两个弯的相同图形:
(图片取自编程之美)
这个问题的结构与迷宫问题有点类似,可是为了应用BFS,我们的大脑也得转个弯,玩家依次点了两个结点后,我们需要判断是否可以消去:
- 图形是否相等非常简单,只要判断该处元素的值即可
- 相连是否不超过两个弯,我们需要从中取一个结点为起始点,先拿到无须转弯就能到达的结点集合A,看目标结点是否在里面;如果不在,则需要对结点集合A中所有结点,拿到其无须转弯就能到达的结点(未在之前访问过),判断目标结点是否在内;如果不在,则继续扩展,这次如果再不在,说明两结点连接超过两个弯了,不满足
此处, vertex是所有没有图形的结点,而edge则是任意两个在同一直线上的,未被有图形的结点截断的结点之间的联系。BFS的应用在于结点距离不超过2次,此处距离是指转弯次数。
通过上诉例子,BFS之强大与有趣可见一斑!
[原]HDOJ 1175 连连看 (bfs)
2012-5-17阅读241 评论0
题目链接:~( ̄▽ ̄~)(~ ̄▽ ̄)~
思路:用bfs一次把这个方向上能到的点入队
http://m.blog.csdn.net/blog/ulquiorra0cifer/7577607
code:
#include <stdio.h>
int m = 0, n = 0, front = 0, rear = 0, map[1002][1002], used[1002][1002];
int X1 = 0, Y1 = 0, X2 = 0, Y2 = 0;
int dir[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
typedef struct
{
int x, y, count;
}node;
node step, quene[1000*1000+1];
int bfs()
{
int i = 0, x = 0, y = 0, fx = 0, fy = 0, count = 0;
quene[rear++] = step;
while(front<rear)
{
x = quene[front].x; y = quene[front].y; count = quene[front++].count;
for(i = 0; i<4; i++)
{
fx = x+dir[i][0]; fy = y+dir[i][1];
while(!map[fx][fy] && count<3 && fx>0 && fx<=n && fy>0 &&fy<=m && !used[fx][fy])//这方向上能到的点入队
{
used[fx][fy] = 1;
step.x = fx; step.y = fy; step.count = count+1;
quene[rear++] = step;
fx += dir[i][0]; fy += dir[i][1];
}
if(fx == X2 && fy == Y2 && count<3)
return 1;
}
}
return 0;
}
int main()
{
int i = 0, j= 0, q = 0;
while(scanf("%d %d",&n, &m) , n && m)
{
for(i = 1; i<=n; i++)
for(j = 1; j<=m; j++)
scanf("%d",&map[i][j]);
scanf("%d",&q);
while(q--)
{
for(i = 1; i<=n; i++)
for(j = 1; j<=m; j++)
used[i][j] = 0;
scanf("%d %d %d %d",&X1, &Y1, &X2, &Y2);
if((map[X1][Y1] != map[X2][Y2] || !map[X1][Y1] || !map[X2][Y2]) || (X1==X2 && Y2 == Y1))
printf("NO\n");
else
{
front = rear = 0;
step.x = X1; step.y = Y1;step.count = 0;
if(bfs())
printf("YES\n");
else
printf("NO\n");
}
}
}
return 0;
}
http://blog.chinaunix.net/uid-26602509-id-3179280.htmlhttp://m.blog.csdn.net/blog/ulquiorra0cifer/7577607http://m.blog.csdn.net/blog/ulquiorra0cifer/7577607
分析起来,以上两种算法有两种思想,一种是每个点按照四个方向搜索四遍,分别记录四遍过程中转向次数最少的放入hash当中,如果其中有情况是结果比hash大了,说明这个方向搜索到这个点路径不好。
一种是按照点来的,一个点按照四个方向搜索,搜出4个点放进去