传送门:http://poj.org/problem?id=2488
题目大意:在一个M X N的矩阵中,骑士需要从其中任意点出发,遍历所有点。
骑士的走路规则:沿某一个方向A走两步,再沿这个方向的垂直方向B走一步。(A方向的两步不算,即一次到达最后点)
图示:假设骑士在中间位置,则带有白色圆圈的8个点表示可以一次到达的位置。
此题似乎只要找到一个能遍历的方法即可,但是题目的输出特别要求lexicographically,即按照字典顺序,竖直方向为1→M,水平方向为ABC……(共N个)。
刚开始,这题我也没弄懂什么意思,在百度上看到很多大牛的解题报告,很多(±2,±1)的坐标我也没弄懂,现在回过头来看终于明白这些坐标及其放置顺序的意义:即依次按照我们已经给定的最优顺序来向下纵深的回溯,而这种最优顺序的最终结果是使输出按照最低的字典顺序。列图如下:
如上图所示,在8个圆圈中,分别标记1-8,代表的即是坐标{(-2,-1),(-2,1),(-1,-2),(-1,2),(1,-2),(1,2),(2,-1),(2,1)} (x,y)表示横坐标移动x位,纵坐标移动y位。按照这个顺序搜寻可以得到最优解。
了解了题意之后我们就可以比较轻松的分析出解法了:
一、建立解空间,即上述表格,可以用一个二维数组visit[30][30],因为题目要求p*q<=26,因此p和q都是小于等于26的;
二、要得到最优顺序,必须先从A1开始搜索,因此令visit[1][1] = 1;
三、按照回溯法的步骤此时,backtrace(1,1,1)前两个1代表此时坐标为1,1,第三个代表此时才搜索了1个位置
四、在backtrace()中,当搜索长度length = r * c时,代表已经遍历完所有结点,这时我们令flag = 1,便于在最后决定这个表格是否具有最优解。
五、当length <= r * c时,则需要依次按照上述8个坐标的顺序搜索,并记下每次的x和y坐标。
算法如下:
#include <stdio.h>
int r , c ;//行、列
int visit[30][30];//visit[i][j] = 0表示未被访问,等于1表示被访问过
int xx[8] = {-2,-2,-1,-1,1,1,2,2};
int yy[8] = {-1,1,-2,2,-2,2,-1,1};
int px[30] , py[30];
int flag ;
int px[30],py[30];
int place(int x , int y)
{
return x >= 1 && x <= c && y >=1 && y <= r ;
}
void backtrace(int x , int y , int length)
//分别表示当前搜索点的横纵坐标及搜索长度
{
int m , n , i ;
px[length] = x;
py[length] = y;
if(length == r * c)
{
flag = 1 ;
return;
}
for(i = 0 ; i < 8 ; i ++)
//依次按照8个优先顺序搜索
{
m = x + xx[i];
n = y + yy[i];
if(place(m,n)&&!visit[m][n]&&!flag)
//m,n点未被访问,且可搜索
{
visit[m][n] = 1;
backtrace(m,n,length+1);
visit[m][n] = 0;//还原之,假设进入下一个结点
}
}
}
int main()
{
int n , i = 1 , j;
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&r,&c);
flag = 0 ;
memset(visit,0,sizeof(visit));
printf("Scenario #%d:\n",i++);
visit[1][1] = 1;//首先访问A1
backtrace(1,1,1);
if(flag)
{
for(j = 1 ; j <= r * c ; j ++)
{
printf("%c",px[j]+'A'-1);
printf("%d",py[j]);
}
}
else
printf("impossible");
printf("\n\n");
}
}