DFS—poj2488

解法一:与POJ3009 类似,都要先置位,(然后执行本次要完成的任务),最后复位!!

#include<iostream>
using namespace std;

typedef class
{
	public:
		int row;
		char col;
}location;

int p,q;  //chess size = p*q
          //数字是行p,字母是列q
bool chess['Z'+1][27];

int x,y;  //返回值
void path(int i,int j,int num)  //ij为骑士当前在棋盘的位置 
{                               //num为骑士即将要跳到的位置序号
	switch(num)
	{
     	case 1: {x=i-1; y=j-2; break;}     //注意这个尝试跳的顺序不能错   
		case 2: {x=i+1; y=j-2; break;}     //因为题目要求是字典序lexicographically输出
		case 3: {x=i-2; y=j-1; break;}     //这个顺序错了,必定WA
		case 4: {x=i+2; y=j-1; break;}
		case 5: {x=i-2; y=j+1; break;}
		case 6: {x=i+2; y=j+1; break;}
		case 7: {x=i-1; y=j+2; break;}
		case 8: {x=i+1; y=j+2; break;}
	}
	return;
}

bool DFS(location* way,int i,int j,int step)
{
	chess[i][j]=true;
	way[step].row=i;
	way[step].col=j;
	if(step==way[0].row)
		return true;

	for(int k=1;k<=8;k++)   //骑士从当前位置尝试跳到其他位置
	{
		path(i,j,k);
		int ii=x,jj=y;
		if(!chess[ii][jj] && ii>=1 && ii<=p && jj>='A' && jj<='A'+q-1)
			if(DFS(way,ii,jj,step+1))
				return true;
	}
	
	chess[i][j]=false;  //能执行到这步,说明前面跳的8步都不符合要求
	return false;       //即当前位置是错误位置,擦除记录返回上一步
}

int main(void)
{
	int test;
	cin>>test;
	int t=1;
	while(t<=test)
	{
		/*Initial*/

		memset(chess,false,sizeof(chess));

		cin>>p>>q;
		if(p==1 && q==1)      //范围缩窄,不要也能AC
		{
			cout<<"Scenario #"<<t++<<':'<<endl;
			cout<<"A1"<<endl<<endl;
			continue;
		}
		if(p*q>26 || p>=9 || q>=9 || p<=2 || q<=2)        //范围缩窄,不要也能AC
		{
			cout<<"Scenario #"<<t++<<':'<<endl;
			cout<<"impossible"<<endl<<endl;
			continue;
		}
		
		location* way=new location[p*q+1];   //记录走过的位置坐标
		way[0].row=p*q;   //记录总步数(棋盘总格子数)

		/*DFS*/

		bool flag=false;
		for(int j='A';j<='A'+q-1;j++)
		{
			for(int i=1;i<=p;i++)
				if(DFS(way,i,j,1))
				{
					cout<<"Scenario #"<<t++<<':'<<endl;
					
					for(int k=1;k<=way[0].row;k++)
						cout<<way[k].col<<way[k].row;
					cout<<endl<<endl;
					flag=true;
					break;
				}
				if(flag)
					break;
		}

		if(!flag)
		{
			cout<<"Scenario #"<<t++<<':'<<endl;
			cout<<"impossible"<<endl<<endl;
		}
	}
	return 0;
}

下面这张图解释在涂白之后会不会重复记录( way [ step ] )的问题,答案是不会!

跳马是双向的,因此是无向图。这就注定了,当某个结点由黑被涂白之后,是不可能再被重新访问的。

如图中的结点3,假设它没有发现4,导致其被涂白回溯到结点2,此时由结点2再访问结点4,因为结点3已经被涂白了,由结点4就会再次重复访问记录结点3。

这种担心是多余的,或者说根本不可能发生。因为既然由4能访问到3,那么由3也必然能访问到4,就不可能漏掉这个结点,所以假设根本就不成立,故不可能重复访问。




解法二:

先上代码poj2488

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;

#define maxn 26

struct Point
{
    int x, y;
} way[maxn * maxn];

bool map[maxn][maxn];
int p, q;
bool found;
int dir[8][2] =
{
{ -2, -1 },
{ -2, 1 },
{ -1, -2 },
{ -1, 2 },
{ 1, -2 },
{ 1, 2 },
{ 2, -1 },
{ 2, 1 } };

bool ok(int x, int y)
{
    if (x < 0 || y < 0 || x >= q || y >= p)
        return false;
    if (map[x][y])
        return false;
    return true;
}

void dfs(int x, int y, int step)
{
    way[step].x = x;
    way[step].y = y;
    if (step == p * q - 1)
    {
        found = true;
        return;
    }
    for (int i = 0; i < 8; i++)
        if (ok(x + dir[i][0], y + dir[i][1]))
        {
            map[x + dir[i][0]][y + dir[i][1]] = true;
            dfs(x + dir[i][0], y + dir[i][1], step + 1);
            if (found)
                return;
            map[x + dir[i][0]][y + dir[i][1]] = false;
        }
}

void print()
{
    for (int i = 0; i < p * q; i++)
        printf("%c%d", way[i].x + 'A', way[i].y + 1);
    printf("\n\n");
}

int main()
{
    //freopen("t.txt", "r", stdin);
    int t;
    scanf("%d", &t);
    int s = 0;
    while (t--)
    {
        s++;
        printf("Scenario #%d:\n", s);
        memset(map, 0, sizeof(map));
        scanf("%d%d", &p, &q);
        for (int i = 0; i < q; i++)
        {
            for (int j = 0; j < p; j++)
            {
                found = false;
                map[i][j] = true;
                dfs(i, j, 0);
                if (found)
                    break;
                map[i][j] = false;
            }
            if (found)
                break;
        }
        if (found)
            print();
        else
            printf("impossible\n\n");
    }
    return 0;
}

下面以“图“来说明DFS的执行过程,约定

1)涂黑即为map置1,表示方格已访问过,反之白色为map复位,表示返回到上一层或者未访问过的方格;、

2)不带圆圈的数字(实质是 way[step] 的”填充“过程)表示涂黑的顺序,带圆圈的表示由黑转白的顺序。


注:1)如果我们最终无法找到一个完整的路径覆盖所有的方格,即step< p * q - 1,最终的结果一定是所有已涂黑的结点全部转为白色,供下一次的递归入口(i , j)重新使用(这也是为什么转白的原因),至于本次的递归入口也会转白,不过是在主函数中完成的:map[i][j] = false ; 每次进入递归入口时step都会初始化为0,dfs(i, j,0);;

        2)如果找到了则最终的结点颜色分布可能是下面的两种情况中的一种:要么所有的结点都是黑色,要么有的结点是黑色有的结点是由黑转白变成的白色(这是由回溯造成的),而绝无可能一直是白色。只要知道了完整路径,我们是不在乎颜色分布的,因为一旦step= p * q - 1,就会一路return ; ,直到返回到主函数中 print( ); 完成本次使命;

        3)所有的涂黑(从上一层进入时)和黑转白(退回到上一层时)操作都是由其前驱结点来完成的;每次都是先把控制权交由上一层递归(说明下面没有后继结点了,“回溯”),然后再由黑转白,这两件事必然要发生都发生

              如上图中,②要变白之前,先返回到上一层递归,即1所在处,把控制权交给1,然后由黑转白;

        4)一旦进入每次的递归入口,step就会一直累加,而不会被覆盖,即使在”回溯“的情况下。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值