HDOJ 1175 连连看 广度优先搜索

来源:互联网 发布:易趣和淘宝的区别 编辑:程序博客网 时间:2024/06/06 08:28

http://www.cppblog.com/mythit/archive/2009/04/29/81497.html?opt=admin

Problem Description
“连连看”相信很多人都玩过。没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子。如果某两个相同的棋子,可以通过一条线连起来(这条线不能经过其它棋子),而且线的转折次数不超过两次,那么这两个棋子就可以在棋盘上消去。不好意思,由于我以前没有玩过连连看,咨询了同学的意见,连线不能从外面绕过去的,但事实上这是错的。现在已经酿成大祸,就只能将错就错了,连线不能从外围绕过。
玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序。


 

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 41 2 3 40 0 0 04 3 2 141 1 3 41 1 2 41 1 3 32 1 2 43 40 1 4 30 2 4 10 0 0 021 1 2 41 3 2 30 0


 

Sample Output
YESNONONONOYES

    一看题目就知道是用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个点放进去

原创粉丝点击