题目大意:
给出马的初始位置,得出遍历棋盘的一条路径。
解题思路:
DFS(深搜)+剪枝。
1、DFS
I. 计算初始状态root下步可达点
II. 如果下步可达,则继续计算下步的下步是否可达
III. 如果下步不可达,则返回上一步计算其他方向下一步是否可达
IV. 步数不为30,则重复II/III。
2、剪枝
仅仅使用DFS,递归效率低下。
在进行下一步递归时,同时计算下下步可达点数,并按可达点数进行排序。
可达点数少,则意味着进行递归的次数及深度少。
先递归可达点数少的步,在有些情况下可以有效地减少递归时间。
// 1153.马周游
// 遍历之前进行下步可达点计算,并排序
// 可达点较少,意味着遍历次数及深度较少
#include <iostream>
#include <vector>
#include <memory.h>
#include <algorithm>
using namespace std;
struct Position{
int x,y,steps;
};
// 数组代替switch计算方向
int move_x[] = {-1, -1, -2, -2, 1, 1, 2, 2};
int move_y[] = {-2, 2, 1, -1, 2, -2, 1, -1};
int isTouched[10][10];
int path[65];
// 判断下步是否可达
bool isLegal( const Position p ){
return ( ( p.x >= 1 && p.x <= 8 ) && ( p.y>=1 && p.y <= 8 ) && !isTouched[p.x][p.y] );
}
// 比较借点下步可达数
bool comp( const Position& p1, const Position& p2 ){
return p1.steps < p2.steps;
}
bool movement( Position curr,int step ) {
if( step == 64 ){
cout << path[0];
for( int i = 1; i < 64; ++i )
cout << " " << path[i];
cout << endl; // 这有一行空行。。。这也WA。。
return true;
}
vector<Position> nexts;
Position next;
for( int i = 0; i < 8; i++ ){
next.x = curr.x + move_x[i];
next.y = curr.y + move_y[i];
next.steps = 0;
// 如果下一步可达,则计算下一步有多少可达点
if( isLegal(next) ){
Position tmp;
for( int j = 0; j < 8; j++ ){
tmp = next;
tmp.x = next.x + move_x[j];
tmp.y = next.y + move_y[j];
if( isLegal(tmp) ){
next.steps++;
}
}
nexts.push_back(next);
}
}
// 根据下步可达点进行排序
sort(nexts.begin(), nexts.end(), comp);
for( int i = 0; i < nexts.size(); i++ ){
Position next = nexts[i];
isTouched[next.x][next.y] = 1;
path[step] = (next.x - 1)*8 + next.y;
if( movement(next,step+1) ) return true; // 注意递归结束条件!
isTouched[next.x][next.y] = 0;
}
return false;
}
int main () {
//freopen("D:\\input.txt","r",stdin);
int start;
Position root,curr,next;
while( cin >> start && start != -1 ){
memset(isTouched,0,sizeof(isTouched));
path[0] = start;
start--;
root.x = start / 8 + 1;
root.y = start % 8 + 1;
isTouched[root.x][root.y] = 1;
movement(root,1);
}
return 0;
}