题目链接在此
用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; }