POJ-2488 A Knight's Journey 解题报告(搜索) 马跳遍棋格的字典序问题

本文介绍了一种解决骑士周游问题的方法,即骑士如何在限定尺寸的棋盘上不重复地走过每一个格子。通过深度优先搜索(DFS)结合回溯算法实现,确保路径按字典序输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A - A Knight's Journey

Time Limit:1000MS    Memory Limit:65536KB    64bit IO Format:%I64d & %I64u

http://openoj.awaysoft.com:8080/judge/problem/viewProblem.action?id=23456

 

Description

Background
The knight is getting bored of seeing the same black and white squares again and again and has decided to make a journey
around the world. Whenever a knight moves, it is two squares in one direction and one square perpendicular to this. The world of a knight is the chessboard he is living on. Our knight lives on a chessboard that has a smaller area than a regular 8 * 8 board, but it is still rectangular. Can you help this adventurous knight to make travel plans?

Problem
Find a path such that the knight visits every square once. The knight can start and end on any square of the board.

Input

The input begins with a positive integer n in the first line. The following lines contain n test cases. Each test case consists of a single line with two positive integers p and q, such that 1 <= p * q <= 26. This represents a p * q chessboard, where p describes how many different square numbers 1, . . . , p exist, q describes how many different square letters exist. These are the first q letters of the Latin alphabet: A, . . .

Output

The output for every scenario begins with a line containing "Scenario #i:", where i is the number of the scenario starting at 1. Then print a single line containing the lexicographically first path that visits all squares of the chessboard with knight moves followed by an empty line. The path should be given on a single line by concatenating the names of the visited squares. Each square name consists of a capital letter followed by a number.
If no such path exist, you should output impossible on a single line.

Sample Input

3
1 1
2 3
4 3

Sample Output

Scenario #1:
A1

Scenario #2:
impossible

Scenario #3:
A1B3C1A2B4C2A3B1C3A4B2C4

Hint

题目大意:

给出一个棋盘,判断骑士能否不重复的走过所有格,只能按照中国象棋中马的走法

,并记录下其中按字典序排列的第一种路径。

 

 

解题思路:

DFS+回溯,但是要注意方向。另外还要注意下面几点:

(1) 题目要求以"lexicographically"方式输出,也就是字典序,要以字典序输出路径,那么方向数组就要以特殊的顺序排列了...这样只要每次从dfs(1,1)开始搜索,第一个成功遍历的路径一定是以字典序排列...

(2) 横行为字母,表示横行坐标的是y;纵行为数字,表示纵行的坐标是x

代码:

 

解析参考:http://blog.chinaunix.net/uid-20776510-id-1846480.html

代码参考:http://www.lceve.com/?cat=37

#include "stdio.h" 
#include "string.h"

//八个方向 
int dir[8][2] = {{-1, -2}, {1, -2}, {-2, -1}, {2, -1},
                {-2, 1}, {2, 1}, {-1, 2}, {1, 2}};

int num, p, q;
bool map[30][30]; //标记某个点是否去过 
int x[30], y[30]; //储存路径 

bool dfs(int xx, int yy, int ans){
    x[ans] = xx;
    y[ans] = yy;
    map[xx][yy] = false;
    
    //递归结束 
    if(ans >= num)
        return true;
    
    //八个方向深搜 
    for(int i = 0; i < 8; ++i){
        int a = xx + dir[i][0];
        int b = yy + dir[i][1];
 
        if(a > 0 && b > 0 && a <= p && b <= q && map[a][b])
            if(dfs(a, b, ans + 1))
                return true;
    }
    
    map[xx][yy] = true;
    return false;
}

int main(void){
    int t, test = 0;
    scanf("%d", &t);
    
    while(t--){
        memset(map, true, sizeof(map));
        
        scanf("%d%d", &p, &q);
        num = p * q;
        
        if(test) puts("");
        printf("Scenario #%d:\n", ++test);
        
        if(dfs(1, 1, 1)){
            //打印路径 
            for(int i = 1; i <= num; ++i)
                printf("%c%d", y[i] + 'A' - 1, x[i]);

            printf("\n");
        }
        else{
            puts("impossible");
        }
    }
    
    return 0;
}


 

交一个自己的版本,感觉比较繁琐,提交了好几次才发现是没有回溯。。。 没有cur--怎么行呢。。。。

 

#include<stdio.h>
#include<string.h>
int vis[30][30];
char med[900][2];
int x,y,cur=0,sum,v=0;
int dir[8][2]={{-1,-2},{1,-2},{-2,-1},{2,-1},{-2,1},{2,1},{-1,2},{1,2}};
void DFS(int i,int j)
{
	int k;
	if(v)return;
	cur++;
	vis[i][j]=1;   //标记结构
	med[cur][0]=i+'1'-1;med[cur][1]=j+'A'-1; //记录路径
	if(cur==sum) {v=1; return;}//这个v很重要!!表示已经找到了!!
	for(k=0;k<8;k++)  //按八方向查找
	{
		int a=i+dir[k][0],b=j+dir[k][1];
		if(!vis[a][b]&& a<=x && a>0 && b<=y && b>0)
		{ 
			DFS(a,b);cur--;//既然是回溯就要cur--啊 = =!
		}
	}vis[i][j]=0;return; //回溯,消除标记!
}
int main()
{
	int n,jishu=1;
	scanf("%d",&n);
	int m=n;
	while(n--)
	{
		scanf("%d%d",&x,&y);
		memset(vis,0,sizeof(vis));
		memset(med,0,sizeof(med));
		sum=x*y;
		med[0][0]='1',med[0][1]='A',vis[1][1]=1;
		DFS(1,1);
		printf("Scenario #%d:\n",jishu++);
		if(v)  //说了这个v很重要吧。。。。
			for(int p=1;p<=sum;p++) printf("%c%c",med[p][1],med[p][0]);
		else
			printf("impossible");
		printf("\n");
		if(jishu-1<m)printf("\n");
		cur=0,v=0;
	}
	return 0;
}



 

 

后来是看的别人的代码啊~~ 总结一下觉得第一种最好,不要考虑回溯~~

这是别人做的。。。

友情链接啊。。。http://blog.youkuaiyun.com/xiaoyu_93?viewmode=contents

 

#include<iostream>
using namespace std;
int visited[27][27];      //标记是否访问过
int dir[8][2]={{-1,-2},{1,-2},{-2,-1},{2,-1},{-2,1},{2,1},{-1,2},{1,2}};   // 骑士移动的8个方向 (按字典序排列)
int step,visite;     //step表示已游历的区域数目,visite用来记录是否有可行的路径
int x[27],y[27];      // 访问的位置坐标
int n,p,q,i,j;
void dfs(int r,int l)  //搜索
{
	int m,n,i;
	if(visite) return;
	step++; //step+1
	x[step]=r;  
	y[step]=l;  //记录当前位置
	if(step==p*q)  //符合要求 答案已找到
	{
		visite=1;
		return;
	}
	visited[r][l]=1;  //标记为已游历
	for(i=0;i<8;i++)
	{
		m=l+dir[i][0];   // y方向
		n=r+dir[i][1];  //x方向
		if(visited[n][m]==0&&n>0&&n<=p&&m>0&&m<=q)  //下个游历的区域未走过且未超出区域
		{
			dfs(n,m);
			step--;  //回溯不符合,step-1
		}
	}
	visited[r][l]=0;//返回时,重置为未访问
	return;
}
int main()
{
	cin>>n;
	for(i=1;i<=n;i++)
	{
		step=0;
		visite=0;
		cin>>q>>p;
		dfs(1,1); 
		cout<<"Scenario #"<<i<<":"<<endl;
		if(visite)
		{
			for(j=1;j<=p*q;j++)
				cout<<(char)(x[j]+64)<<y[j];  
		/*   国际标准,横行应该是y,但为了适应习惯,
		 我把输入的p,q换了位置,所以这里输出还是横行为x   */
			cout<<endl;                         
		}
		else
			cout<<"impossible"<<endl;
		if(i!=n)
			cout<<endl;
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值