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
给出一个棋盘,判断骑士能否不重复的走过所有格,只能按照中国象棋中马的走法
,并记录下其中按字典序排列的第一种路径。

解题思路:
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;
}