题目链接在此
用1152的解法做果断TLE,然后优化了一下,采用了类似启发式搜索的做法,答案秒出。
数据结构与算法思想
(1) 数据结构
①用结构体记录每个位置,其中包含其坐标:
struct pos {
int row;
int col;
};
②用数组存储是否访问过某位置:
bool visited[65];
③用数组记录“周游”的顺序:
int printSeq[65];
(2)算法思想
①使用DFS(深度优先搜索算法)。
②在搜多的过程中采用“启发式搜索”,以减少回溯次数。
详细解题思路
①DFS:从起点开始,搜索下一步可访问且未被访问的位置,直到所有位置都已经被访问过。
②启发式搜索:获取当前位置的下一步可移动至的所有位置,将这些所有位置排序,按这个顺序接着进行DFS。排序的标准:按这些位置的下一步可移动至的所有位置的多少,由少到多排序。
逐步求精算法描述
本题解法的关键在于深度优先搜索算法:
①初始化:将起点标记为已访问,周游顺序数组的第一项记为起点的数字标号。 同时,访问计数器设为2(目的是方便记录周游顺序);
②a.若访问计数器为65,则说明周游完成,输出答案并跳出步骤②。
b.找出当前位置下一步能访问且未被访问的所有位置,并按“启发式搜索”中描 述的排序标准给这些位置进行排序。
c.将步骤b中已排序的每个位置(记为next)逐一访问,即:
I.将位置next记为已访问;
II.周游顺序数组的第counter(访问计数器)项记为next的数字标号;
III.访问计数器自增1;
IV.将next做DFS,即从步骤a开始循环
V.回溯:将位置next记为未访问;
VI.访问计数器自减1;
源代码与注释:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct pos {
int row;
int col;
// 结构体构造函数
pos() {}
pos(int r, int c) : row(r), col(c) {}
// 结构体自定义函数,两位置横纵坐标互相相加
pos plus(pos a) {
return pos(a.row + this->row, a.col + this->col);
}
};
bool visited[65]; // 访问数组
int printSeq[65]; // 周游顺序
const struct pos available[] = { pos(1, -2), pos(2, -1), pos(2, 1), pos(1, 2),
pos(-1, 2), pos(-2, 1), pos(-2, -1), pos(-1, -2) }; // 马可移动的8个方向的坐标
// 函数:将位置的数字标号解析成横纵坐标,构造pos结构体返回
struct pos one_to_two_dim(int n) {
int r = (n - 1) / 8 + 1;
int c = (n - 1) % 8 + 1;
return pos(r, c);
}
// 函数:将位置的横纵坐标解析,
// 若合法(横纵坐标都在[1,8]之间)返回其对应的数字标号,否则返回0
int two_to_one_dim(struct pos p) {
if (p.row >= 1 && p.row <= 8 && p.col >= 1 && p.col <= 8)
return (p.row - 1) * 8 + p.col;
else
return 0;
}
// 函数:接收位置的数字标号,找出其下一步能访问且未被访问的位置的数字标号,
// 存于一个vector中返回
void canMove(int n, vector<int>& result) {
for (int i = 0; i < 8; i++) {
struct pos tmp = one_to_two_dim(n);
int going_to = two_to_one_dim(tmp.plus(available[i]));
if (going_to >= 1 && going_to <= 64 && visited[going_to] == false)
result.push_back(going_to);
}
}
// 函数:接收位置的数字标号,找出其下一步能访问且未被访问的位置的总数量,并返回
int countCanMove(int n) {
int result = 0;
for (int i = 0; i < 8; i++) {
struct pos tmp = one_to_two_dim(n);
int going_to = two_to_one_dim(tmp.plus(available[i]));
if (going_to >= 1 && going_to <= 64 && visited[going_to] == false)
result++;
}
return result;
}
// 自定义比较函数,用于启发式搜索前的排序:
// 接受两个位置的数字标号,若前者下一步能访问且未被访问的位置的总数量大于后者,返回真,否则返回假。
bool myCmp(int a, int b) {
return countCanMove(a) < countCanMove(b);
}
// 深度优先搜寻算法。传入:当前位置的数字标号,是否完成搜索(布尔值),访问计数器
void DFS(int n, bool& isDone, int counter) {
if (isDone) // 若已完成搜索则返回
return;
if (counter == 65) { // 若访问计数器为65,周游完成,输出结果
isDone = true; // 是否完成搜索的布尔值设为真
bool spaceFlag = false;
for (int i = 1; i <= 64; i++) { // 利用周游顺序数组逐一输出结果
if (!spaceFlag)
spaceFlag = true;
else
cout << ' ';
cout << printSeq[i];
}
cout << endl;
}
else {
vector<int> canGoTo;
// 找出当前位置下一步能访问且未被访问的所有位置,数字标号存于canGoTo容器中
canMove(n, canGoTo);
// 按“启发式搜索”中描述的排序标准给canGoTo容器中的位置排序。
sort(canGoTo.begin(), canGoTo.end(), myCmp);
for (int i = 0; i < canGoTo.size(); i++) {
visited[canGoTo[i]] = true; // 将位置canGoTo[i]记为已访问
printSeq[counter] = canGoTo[i]; //周游顺序数组的第counter项记为canGoTo[i]
counter++; // 访问计数器自增1
DFS(canGoTo[i], isDone, counter); // 将canGoTo[i]做DFS
visited[canGoTo[i]] = false; // 回溯:将位置canGoTo[i]记为未访问
counter--; // 访问计数器自减1
}
}
}
int main() {
int start;
cin >> start;
while (start != -1) {
bool isDone = false; // 初始化标记是否完成搜索的布尔值
for (int i = 1; i <= 64; i++) { // 初始化周游顺序数组以及访问数组
printSeq[i] = 0;
visited[i] = false;
}
printSeq[1] = start; // 周游顺序数组的第一项记为起点的数字标号
visited[start] = true; // 将起点标记为已访问
DFS(start, isDone, 2); // 对起点进行DFS
cin >> start;
}
return 0;
}