题目类型 搜索题
题目意思
给你一个p*q的国际象棋棋盘 输出一个字典序最小的方案 (行编号为字母 列编号为数字 字母+数字等于一个棋子的位置) | 其中 1 <= p * q <= 26
这个方案要求 马从起点跳到终点中间经过棋盘所有的点且不重复(起点终点可以自己选定)
解题方法
基础DFS
题目既然要求方案要字典序最小 那么我们就从最小编号的起点开始搜索 即A1
搜索的过程中为了保证字典序最小我们必须制定合适的搜索顺序
题目输入的p是列的数量 q是行的数量那么我们定义 行下标 R = q 列下标 C=p
起点是A1 那么下一个测试点应该 是 R-2 C-1 才可保证字典序最小 (因为最终是字母即行号优先小的)
所以搜索的顺序是 [R-2,C-1] -> [R-2,C+1] -> [R-1, C-2] -> [R-1, C+2] -> [R+1, C-2] -> [R+1, C+2] -> [R+2, C-1] -> [R+2, C+1]
即我们的 dx数组是 dx[] = {-2, -2, -1, -1, 1, 1, 2, 2} dy数组是 dy[] = {-1, 1, -2, 2, -2, 2, -1, 1}
本题如果有解的话直接从最小编号开始搜索即可找到最优解且第一个找到的解就是最优解, 找不到合法解的时候记得回溯
下面解释来自
z3635363
我们来考虑这样一个问题,假设从 P 可以到所有点 ,而从 Q 不能到所有点,假设 Q 不能到 W 点。
显然 W 不能等于 P
则问题等价于到 W 的点完全被用过,而从未到 W ,这种情况只能是因为只有一个点可以通往 W,因为其他情况我们可以先到 W 再回去。而 P 可以到所有点,说明 从 P 开始至少可以找到两个点可以到 W,矛盾。
所以,若从P 开始可以到所有点,则从任一点开始可以到所有点。
参考代码 - 有疑问的地方在下方留言 看到会尽快回复的
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int r, c;
int dx[8] = {-2, -2, -1, -1, 1, 1, 2, 2};
int dy[8] = {-1, 1, -2, 2, -2, 2, -1, 1};
bool vis[30][30];
int road[1000][2];
int count;
bool DFS(int x, int y) {
if(count == r * c) return true;
for( int i=0; i<8; i++ ) {
int tx = x + dx[i];
int ty = y + dy[i];
if(tx >= 0 && tx < r && ty >= 0 && ty < c && !vis[tx][ty]) {
vis[tx][ty] = 1;
road[count][0] = tx; road[count++][1] = ty;
if(DFS(tx, ty)) return true;
vis[tx][ty] = 0;
count--;
}
}
return false;
}
int main() {
int t, cnt = 1;
scanf("%d", &t);
while(t--) {
scanf("%d%d", &c, &r);
printf("Scenario #%d:\n", cnt++);
memset(vis, 0, sizeof(vis));
count = 1;
vis[0][0] = true;
road[0][0] = 0, road[0][1] = 0;
if(DFS(0, 0)) {
for( int i=0; i<r*c; i++ ) printf("%c%d", road[i][0]+'A',road[i][1]+1);
printf("\n");
}
else printf("impossible\n");
printf("\n");
}
return 0;
}