题目大意
骑士厌倦了一次又一次看到相同的黑白方块,并决定踏上旅途
世界各地。 每当骑士移动时,它在一个方向上是两个正方形,而垂直于该方向的正方形是一个正方形。 骑士的世界就是他赖以生存的棋盘。 我们的骑士生活在棋盘上,棋盘的面积比普通的8 * 8棋盘小,但它仍然是矩形的。 您可以帮助这个冒险的骑士制定旅行计划吗?
问题
找到一条让骑士访问每个广场一次的路径。 骑士可以在棋盘的任何正方形上开始和结束。
思路分析
首先要走完全程我们显然可以搜索,这道题普通的dfs就足够了,然而有一个地方比较棘手,就是要按字典序输出结果。怎么做呢?
- 因为我们起点不确定,因此从上到下,从左到右遍历起始点,这样可以保证第一个点总是字典序最小的起始点/
- 对一次搜索内部,
dx[8] = { -2,-2,-1,-1,1,1,2,2 }, dy[8] = { -1,1,-2,2,-2,2,-1,1 };
,仔细看这八个策略,我们是从上到小,从左到右的次序,也就是说第一个合格的策略总是字典序最小的。 - 然后就要注意dfs的回溯了,我们在函数的开始将当前位置加入路径,如果找不到一个到重点的方案,记着重新把路径返回前一状态。
#include<iostream>
#include<string.h>
#include<string>
using namespace std;
#define MAX 30
#define ll int
// x从上到下,y从左到右进行变化,就可以满足字典序
ll dx[8] = { -2,-2,-1,-1,1,1,2,2 }, dy[8] = { -1,1,-2,2,-2,2,-1,1 };
ll w, l, vis[MAX][MAX];
string s[MAX][MAX], step;
bool check(ll x, ll y) {
if (x <= 0 || x > l || y <= 0 || y > w || vis[x][y])return false;
return true;
}
// 当前点的坐标以及已经遍历的点的数目
ll dfs(ll x, ll y, ll cnt) {
step += s[x][y];
vis[x][y] = 1;
// 结束条件: 走完全部的点
if (cnt + 1 == l * w) return true;
for (int i = 0; i < 8; i++) {
ll xx = x + dx[i], yy = y + dy[i];
if (check(xx, yy)) {
if (dfs(xx, yy, cnt + 1)) return true;
vis[xx][yy] = 0; // 跑完要回溯
}
}
//如果你找不到路就要把step还原
step.erase(step.size() - 2, 2);
return false;
}
int main() {
int m; cin >> m;
//把每个位置对应的字符存储起来
for (int i = 1; i <= 26; i++) {
for (int j = 1; j <= 26; j++) {
s[i][j] = (i - 1 + 'A');
if (j >= 10) s[i][j] += j / 10 + '0';
s[i][j] += j % 10 + '0';
}
}
for (int i = 1; i <= m; i++) {
printf("Scenario #%d:\n", i);
cin >> w >> l; //w行 l列
ll sign = 0;
for (int j = 1; j <= l && !sign; j++) {
for (int k = 1; k <= w && !sign; k++) {
step.clear();
memset(vis, 0, sizeof(vis));
if (dfs(j, k, 0)) sign = 1;
}
}
if (sign) cout << step << endl;
else cout << "impossible" << endl;
cout << endl;
}
}