题解:P16183 [ICPC 2014 NAIPC] Two Knight' s Poem
P16183 题解
题目
这道题的核心是判断两位国际象棋骑士能否在特制键盘上合作“走”出一首给定的诗。
具体规则如下:
-
棋盘与棋子:键盘是一个 \(4\) 行 \(10\) 列的网格。两位骑士分别从最左边和最右边的 Shift 键出发。骑士只能按象棋中“日字步”移动,且不能同时占据同一个按键。
-
按键与输入规则:
- 落在符号键上会输入字符。若另一位骑士恰好站在某个 Shift 键上,则输入该键的上档字符(如大写字母);否则输入下档字符(如小写字母)。
- 落在空格键上始终输入空格(与 Shift 状态无关)。
- 落在 Shift 键上不输入任何字符(仅作为中转站,用于改变另一位骑士的 Shift 状态)。
-
行动顺序:两位骑士可以任意轮流移动,甚至同一位骑士可以连续移动多次。每次移动最多输入一个字符(落在符号或空格上时)。
-
任务与输出:给定一首长度 \(1\) 到 \(100\) 的诗(仅含键盘符号和空格,不以空格开头结尾),判断骑士们能否通过一系列合法移动,依次且精确地输入这首诗的每个字符。能则输出
1,否则输出0。输入包含多组测试用例,以*结束。
做法
考虑设计状态 \(f_{{step},{pos_1},{pos_2}}\) 代表要匹配 \(s\) 的第 \(step\) 位的时候左边的骑士在 \(pos_1\) 的位置,右边的骑士在 \(pos_2\) 的位置可不可做到。
考虑使用 DFS。以左边的为例,遍历所有可以到的地方 \(npos\)。如果 \(npos\) 是 Shift,那么 \(f_{step,npos,pos_2}\) 也为真,继续递归。如果是 \(s_{step}\) 那么 \(f_{step+1,npos,pos_2}\) 也为真。之后继续递归即可。
如果新状态原本也为真,说明已经有分支递归过了,就不递归。
如果 \(step=n\),说明已经全部匹配完成,直接回溯即可。
由于每一个状态只会递归一次,所以复杂度是和状态数成线性关系,总状态数和总字符串大小也成线性关系,且输入大约 12KB,大约 \(1.3\times10^4\),不会 TLE。
代码
#include <bits/stdc++.h>
using namespace std;
bool vis[105][4][10][4][10];
const int DIR[8][2] = {
{1, 2}, {-1, 2}, {1, -2}, {-1, -2},
{2, 1}, {2, -1}, {-2, 1}, {-2, -1}
};
const char kbroad[2][4][11] = {
{"qwertyuiop", "asdfghjkl;", "zxcvbnm,./", "@@ @@"},
{"QWERTYUIOP", "ASDFGHJKL:", "ZXCVBNM<>?", "@@ @@"}
};
int n, flg = 0;
string s;
void dfs(int step, int lx, int ly, int rx, int ry) {
if(step == n) flg = 1;
if(flg) return;
vis[step][lx][ly][rx][ry] = 1;
for(auto [dx, dy] : DIR) {
int nx = lx + dx, ny = ly + dy;
if(nx < 0 || nx > 3 || ny < 0 || ny > 9 || (nx == rx && ny == ry))
continue;
auto nk = kbroad[kbroad[0][rx][ry] == '@'][nx][ny];
if(nk == '@' && !vis[step][nx][ny][rx][ry])
dfs(step, nx, ny, rx, ry);
if(nk == s[step] && !vis[step + 1][nx][ny][rx][ry])
dfs(step + 1, nx, ny, rx, ry);
if(flg) return;
}
for(auto [dx, dy] : DIR) {
int nx = rx + dx, ny = ry + dy;
if(nx < 0 || nx > 3 || ny < 0 || ny > 9 || (nx == lx && ny == ly))
continue;
auto nk = kbroad[kbroad[0][lx][ly] == '@'][nx][ny];
if(nk == '@' && !vis[step][lx][ly][nx][ny])
dfs(step, lx, ly, nx, ny);
if(nk == s[step] && !vis[step + 1][lx][ly][nx][ny])
dfs(step + 1, lx, ly, nx, ny);
if(flg) return;
}
}
int main() {
cin.tie(0)->sync_with_stdio(0);
while(getline(cin, s)) {
if(s == "*") break;
s.pop_back();//去除\r
n = s.size();
for(int i = 0; i <= n; i++)
memset(vis[i], 0, sizeof(vis[i]));
flg = 0;
dfs(0, 3, 0, 3, 9);
cout << flg << "\n";
}
}
题意部分使用了 DeepSeek V4 Flash 进行总结。

浙公网安备 33010602011771号