ACM-搜寻之连连看——hdu1175

ACM-搜索之连连看——hdu1175
连连看
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 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


唉。。一道DFS题目,也可以BFS解。这道题今天搞了一天,有些小悲催。。

刚开始不知道怎么想的,用递归总是超时,最后都出现了栈溢出的错误提示。。。

后来就一直WA。。。好崩溃的说。

最后决定用栈来解这道题目,但发现在判定是否走过的路的数组上出现了问题。

就是你走过的路可以置1,但是当你发现这条路走不通时,如何把走过的路置回0呢?

我没想出来,后来看到别人用队列做的,但他的判断数组设置成了3维的,

最后又加了一个来看 某一点4个方向是否都遍历到,遍历过的置1,这样就避免重复走同一条路的情况。

我最后还是决定用递归做了,恩。。AC后发现7000+MS,跟人家0MS 的比快哭了的说。。

后来,看了看别人的剪枝,放到自己代码上,结果骤减到62MS  ( ⊙o⊙ )!!!!

搜索的精髓果然还是在剪枝上啊!!


这道题难点就在于如何判断转向上,我是设置的dis数组上右下左(0,1,2,3),可以发现 i对2取模,为0时判定是上下走,为1时判定左右走,这样根据之前的方向和现在的方向就可以知道是否转向了。

强大的剪枝就是:

当转向2次后,来判定当前的点和终点 是否在同一条线路,如果不在同一条线路就直接返回。


代码:

#include <stdio.h>
#include <string.h>

int map[1005][1005],vis[1005][1005];
int dis[4][2]={-1,0,0,1,1,0,0,-1};
int n,m,f_x,f_y;
bool ispos;

// dfs开始
void dfs(int x,int y,int direct,int turn)
{
	// 如果转向大于2 或者 已经找到了 则直接返回
    if(turn>2 || ispos==1)  return;
	// 如果到了目标,直接判定可以连接,因为之前转向大于2的都返回了
    if(x==f_x && y==f_y)
    {
        ispos=true;
        return;
    }

	
	// 强大的剪枝啊=。=
	if(turn==2)
	{
		if(x!=f_x && y!=f_y)	return;
		else if(x==f_x)
			if(direct!=1)	return;	
		else if(y==f_y)
			if(direct!=0)	return;
	}


    int i,xx,yy;

	// 四个方向的遍历
    for(i=0;i<4;++i)
    {
        xx=x+dis[i][0];
        yy=y+dis[i][1];
		// 边界判定
        if(xx<1 || yy<1 || xx>n || yy>m || vis[xx][yy]==1)	continue;
		// 如果下一个点是0或者终点
		if(map[xx][yy]==0 || (xx==f_x && yy==f_y))
        {
			vis[xx][yy]=1;

			//根据原来的方向来确定是否转向
            if(direct==-1)  dfs(xx,yy,i%2,turn);
            else if(direct==0)
            {
                if(i%2==1)  dfs(xx,yy,1,turn+1);
                else    dfs(xx,yy,0,turn);
            }
            else if(direct==1)
            {
                if(i%2==0)  dfs(xx,yy,0,turn+1);
                else    dfs(xx,yy,1,turn);
            }

			vis[xx][yy]=0;
        }
    }
}

int main()
{
    int i,j,q,s_x,s_y;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0 && m==0)    break;
        // 输入
		for(i=1;i<=n;++i)
            for(j=1;j<=m;++j)
                scanf("%d",&map[i][j]);

		scanf("%d",&q);
        while(q--)
        {
            scanf("%d%d%d%d",&s_x,&s_y,&f_x,&f_y);

			// 如果搜寻的两个点不相等或者搜寻的点是空点(即搜寻的点有0)
			if(map[s_x][s_y]!=map[f_x][f_y] || map[s_x][s_y]==0 )
			{
				printf("NO\n");
				continue;
			}
			// 如果搜寻两个点都为本身(去掉后增加了点时间)
			if(s_x==f_x && s_y == f_y && map[s_x][s_y]!=0)
			{
				printf("NO\n");  
				continue;
			}

			// 初始化
			memset(vis,0,sizeof(vis));

            ispos=false;
            dfs(s_x,s_y,-1,0);

			// 判断输出
            if(ispos)   printf("YES\n");
            else    printf("NO\n");
        }
    }

    return 0;
}

网上好多人说,这道题的测试数据很水啊。。。

还有WA的可能就是因为方向的问题。。。

提供一组别人想的测试数据:

5 4
0 0 0 0
0 2 2 0
1 0 2 0
2 0 0 0
2 2 0 1
1
3 1 5 4
5 4
2 2 0 1
2 0 0 0
1 0 2 0
0 2 2 0
0 0 0 0
1
1 4 3 1
5 4
1 0 2 2
0 0 0 2
0 2 0 1
0 2 2 0
0 0 0 0
1
1 1 3 4
来源于Pluser的新浪博客