(±2, ±1) 或 (±1, ±2)impossibleScenario #i:/* 8 個騎士移動:預先按「目標格字典序」排序 */
/* 字典序:先比字母(行),再比數字(列) */
/* 行字母 = 'A' + col, 列數字 = row */
int dr[8] = {-2, -2, -1, -1, 1, 1, 2, 2};
int dc[8] = {-1, 1, -2, 2, -2, 2, -1, 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) */
/* 已按 行遞增、列遞增 排序,滿足字典序要求 */
#include <stdio.h>
#include <string.h>
#define MAX_PQ 26
#define MAX_PATH 30
int p, q; // 列數、行數
int visited[7][7]; // 1-based 索引
char path_str[MAX_PATH]; // 字母部分
int path_num[MAX_PATH]; // 數字部分
int path_len; // 當前路徑長度
int found; // 是否找到完整路徑
/* 檢查座標合法且未訪問 */
int valid(int r, int c) {
return r >= 1 && r <= p && c >= 1 && c <= q && !visited[r][c];
}
/* DFS 回溯 */
void dfs(int r, int c) {
if (found) return; // 已找到最優解
visited[r][c] = 1;
path_str[path_len] = 'A' + c - 1;
path_num[path_len] = r;
path_len++;
if (path_len == p * q) { // 訪問完所有格子
found = 1;
return;
}
/* 按預排序方向嘗試 */
for (int i = 0; i < 8; i++) {
int nr = r + dr[i], nc = c + dc[i];
if (valid(nr, nc)) {
dfs(nr, nc);
if (found) return;
}
}
/* 回溯 */
path_len--;
visited[r][c] = 0;
}
int main() {
int T;
scanf("%d", &T);
for (int tc = 1; tc <= T; tc++) {
scanf("%d %d", &p, &q);
printf("Scenario #%d:\n", tc);
if (p == 1 && q == 1) { // 特判 1x1
printf("A1\n\n");
continue;
}
found = 0;
memset(visited, 0, sizeof(visited));
/* 按字典序嘗試所有起點 */
for (int c = 1; c <= q && !found; c++) { // 列 A..
for (int r = 1; r <= p && !found; r++) { // 行 1..
path_len = 0;
dfs(r, c);
}
}
if (found) {
for (int i = 0; i < p * q; i++)
printf("%c%d", path_str[i], path_num[i]);
printf("\n\n");
} else {
printf("impossible\n\n");
}
}
return 0;
}
| 技巧 | 作用 |
|---|---|
| 起點雙重迴圈按字典序 | 保證找到的第一條完整路徑即為全域字典序最小 |
| 方向向量預排序 | 同一起點下,優先嘗試字典序較小的下一步 |
全域 found 剪枝 | 找到解後立即停止所有遞迴,避免多餘搜尋 |
p×q ≤ 26 狀態空間小 | 無需 Warnsdorff 启發式,純 DFS 即可在時限內通過 |
| 特判 1×1 | 避免 DFS 邏輯邊界錯誤 |
若棋盤擴大(如標準 8×8),純 DFS 會超時。引入啟發式:
int count_onward(int r, int c) {
int cnt = 0;
for (int i = 0; i < 8; i++)
if (valid(r + dr[i], c + dc[i])) cnt++;
return cnt;
}
/* 在 dfs 中對合法下一步按 count_onward 升序排序後再遞迴 */