CF1201E2 Knightmare (hard) 题解
题目链接:Knightmare (hard)
题意:
本题是交互题。
Alice 和 Bob 在 \(n \times m\) 的国际象棋棋盘上下棋,\(n,m\) 都是偶数。
棋盘上有一个黑棋 \(\unicode{x265E}\) 和一个白棋 \(\unicode{x2658}\) ,白棋先手,走法同国际象棋中的骑士(同中国象棋的马)。
现在双方要将棋子跳到目标点。白棋的目标点是 \(\left(\frac{n}{2}, \frac{m}{2}\right)\),黑棋的目标点是 \(\left(\frac{n}{2} + 1, \frac{m}{2}\right)\)。
游戏胜利的条件如下:
- 棋子到达了目标点,且当前对方棋子不能攻击到目标点。
- 吃掉了对方的棋子。
如果 \(350\) 回合后游戏未结束,则视为平局。
请你帮助 Alice 选择黑棋 \(\unicode{x265E}\) 或白棋 \(\unicode{x2658}\) 以赢得这场游戏。可以证明一定 Alice 存在必胜策略。
交互格式:
读入 \(n,m,x_0,y_0,x_1,y_1(6\le n,m\le 1000)\) 表示棋盘大小以及白棋黑棋的初始位置 \((x_0,y_0)\) 和 \((x_1,y_1)\) 。
此时你应该输出一行 \(\tt BLACK\) 或者 \(\tt WHITE\) ,表示初始选择的棋子是黑棋 \(\unicode{x265E}\) 还是白棋 \(\unicode{x2658}\)。
当轮到你走棋时,输出 \(x,y\) ,表示走到 \((x,y)\) 。如果你获得了胜利,请立刻结束程序。
请注意:交互器是有适应能力的。也就是说,它的招数可能会随着你的招数的变化而变化。
思维题 + 大模拟。
众所周知,在国际象棋中,当 \(\unicode{x265E}\) 和 \(\unicode{x2658}\) 所处的格子异色时,\(\unicode{x2658}\) 可以吃 \(\unicode{x265E}\) ;反之 \(\unicode{x265E}\) 可以吃 \(\unicode{x2658}\) 。
不妨考虑异色的情况,也就是 \(\unicode{x2658}\) 可以吃 \(\unicode{x265E}\) 的情况。
如果 \(\unicode{x2658}\) 到终点的距离小于等于 \(\unicode{x265E}\) 到终点的距离,那跑就完事了,反正 \(\unicode{x265E}\) 肯定吃不了它。
形式化地,这种情况需要满足 \(\mathrm{dis}(S_\unicode{x2658},T_\unicode{x2658}) \le \mathrm{dis}(S_{\unicode{x265E}}, T_{\unicode{x265E}})\) ,\(S,T\) 表示起点和终点的位置。
反之,\(\unicode{x2658}\) 跑是肯定跑不过了,只能考虑去吃 \(\unicode{x265E}\) 。显然,最好的办法就是往 \(\unicode{x265E}\) 的终点 \(T_{\unicode{x265E}}\) 跑。
这种情况需要满足 \(\mathrm{dis}(S_{\unicode{x2658}}, T_{\unicode{x265E}}) \le \mathrm{dis}(S_{\unicode{x265E}},T_{\unicode{x2658}}) + 1\) ,加 \(1\) 是因为 \(\unicode{x265E}\) 可能到了终点刚好被 \(\unicode{x2658}\) 一步吃掉
如果 \(\unicode{x2658}\) 到了 \(\unicode{x265E}\) 的终点 \(T_{\unicode{x265E}}\) 还没吃掉它,那 \(\unicode{x2658}\) 和 \(\unicode{x265E}\) 肯定距离不少于 \(2\) ,此时他们同色。
然而 \(\unicode{x265E}\) 不可能缩短距离去送死,那么距离就变成了不少于 \(3\) 。
又因为 \(\mathrm{dis}(T_{\unicode{x2658}}, T_{\unicode{x265E}}) = 3\) ,于是 \(\unicode{x2658}\) 在这种情况下,先一口气冲到 \(T_{\unicode{x265E}}\) ,再跳到 \(T_{\unicode{x2658}}\) 就完事了。
什么?上面两个条件都不满足?那还选什么 \(\unicode{x2658}\) ,直接选 \(\unicode{x265E}\) 跑不就完事了。
于是这道题目就变成了一个大模拟。
时间复杂度 \(\mathcal{O}(nm)\)
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
typedef pair<int,int> pii;
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define mem(a, b) memset(a, b, sizeof(a))
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define Fi first
#define Se second
#define N ((int)(1234))
const int dx[] = {-2, -2, -1, -1, 1, 1, 2, 2};
const int dy[] = {1, -1, 2, -2, 2, -2, 1, -1};
int n, m, f1[N][N], f2[N][N]; pii g1[N][N], g2[N][N];
pii get() { int x, y; cin >> x >> y; return {x, y}; }
void put(int x, int y) { cout << x << ' ' << y << endl; }
bool check(int x1, int y1, int x2, int y2) {
for(int i = 0; i < 8; i++) {
if(x1 + dx[i] == x2 && y1 + dy[i] == y2) return true;
} return false;
}
bool safe(int x, int y) { return x > 0 && x <= n && y > 0 && y <= m; };
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
static int x1, y1, x2, y2; cin >> n >> m >> x1 >> y1 >> x2 >> y2;
if(check(x1, y1, x2, y2)) { return cout << "WHITE\n" << x2 << ' ' << y2 << endl, 0; }
mem(f1, -1); mem(f2, -1); static queue<pii> que; f1[x1][y1] = 0; que.push({x1, y1});
while(!que.empty())
{
auto [x, y] = que.front(); que.pop();
for(int i = 0; i < 8; i++)
{
const int tx = x + dx[i], ty = y + dy[i];
if(safe(tx, ty) && f1[tx][ty] == -1) {
f1[tx][ty] = f1[x][y] + 1; g1[tx][ty] = {x, y}; que.push({tx, ty});
}
}
}
f2[x2][y2] = 0; que.push({x2, y2});
while(!que.empty())
{
auto [x, y] = que.front(); que.pop();
for(int i = 0; i < 8; i++)
{
const int tx = x + dx[i], ty = y + dy[i];
if(safe(tx, ty) && f2[tx][ty] == -1) {
f2[tx][ty] = f2[x][y] + 1; g2[tx][ty] = {x, y}; que.push({tx, ty});
}
}
}
if((x1 + y1 + x2 + y2) & 1)
{
if(f1[n / 2][m / 2] <= f2[n / 2 + 1][m / 2])
{
cout << "WHITE" << endl;
vector<pii> v; int x = n / 2, y = m / 2;
while(x != x1 || y != y1) {
v.push_back({x, y}); pii p = g1[x][y]; x = p.Fi, y = p.Se;
}
reverse(v.begin(), v.end());
for(auto p : v)
{
put(p.Fi, p.Se); if(p.Fi == n / 2 && p.Se == m / 2) return 0;
pii q = get(); if(check(p.Fi, p.Se, q.Fi, q.Se)) return put(q.Fi, q.Se), 0;
}
}else if(f1[n / 2 + 1][m / 2] <= f2[n / 2 + 1][m / 2] + 1)
{
cout << "WHITE" << endl;
vector<pii> v; int x = n / 2 + 1, y = m / 2;
while(x != x1 || y != y1) {
v.push_back({x, y}); pii p = g1[x][y]; x = p.Fi, y = p.Se;
}
reverse(v.begin(), v.end());
for(auto p : v)
{
put(p.Fi, p.Se); pii q = get();
if(check(p.Fi, p.Se, q.Fi, q.Se)) return put(q.Fi, q.Se), 0;
}
v.clear(); v.push_back({n / 2, m / 2 + 2});
v.push_back({n / 2 - 2, m / 2 + 1}); v.push_back({n / 2, m / 2});
for(auto p : v)
{
put(p.Fi, p.Se); if(p.Fi == n / 2 && p.Se == m / 2) return 0;
pii q = get(); if(check(p.Fi, p.Se, q.Fi, q.Se)) return put(q.Fi, q.Se), 0;
}
}else
{
cout << "BLACK" << endl;
vector<pii> v; int x = n / 2 + 1, y = m / 2;
while(x != x2 || y != y2) {
v.push_back({x, y}); pii p = g2[x][y]; x = p.Fi, y = p.Se;
}
reverse(v.begin(), v.end());
for(auto p : v)
{
pii q = get(); if(check(x2, y2, q.Fi, q.Se)) return put(q.Fi, q.Se), 0;
put(p.Fi, p.Se); x2 = p.Fi, y2 = p.Se; if(p.Fi == n / 2 + 1 && p.Se == m / 2) return 0;
}
}
}else
{
if(f2[n / 2 + 1][m / 2] < f1[n / 2][m / 2])
{
cout << "BLACK" << endl;
vector<pii> v; int x = n / 2 + 1, y = m / 2;
while(x != x2 || y != y2) {
v.push_back({x, y}); pii p = g2[x][y]; x = p.Fi, y = p.Se;
}
reverse(v.begin(), v.end());
for(auto p : v)
{
pii q = get(); if(check(x2, y2, q.Fi, q.Se)) return put(q.Fi, q.Se), 0;
put(p.Fi, p.Se); x2 = p.Fi, y2 = p.Se; if(p.Fi == n / 2 + 1 && p.Se == m / 2) return 0;
}
}else if(f2[n / 2][m / 2] <= f1[n / 2][m / 2])
{
cout << "BLACK" << endl;
vector<pii> v; int x = n / 2, y = m / 2;
while(x != x2 || y != y2) {
v.push_back({x, y}); pii p = g2[x][y]; x = p.Fi, y = p.Se;
}
reverse(v.begin(), v.end());
for(auto p : v)
{
pii q = get(); if(check(x2, y2, q.Fi, q.Se)) return put(q.Fi, q.Se), 0;
put(p.Fi, p.Se); x2 = p.Fi, y2 = p.Se; if(p.Fi == n / 2 + 1 && p.Se == m / 2) return 0;
}
v.clear(); v.push_back({n / 2 + 1, m / 2 + 2});
v.push_back({n / 2 + 3, m / 2 + 1}); v.push_back({n / 2 + 1, m / 2});
for(auto p : v)
{
pii q = get(); if(check(x2, y2, q.Fi, q.Se)) return put(q.Fi, q.Se), 0;
put(p.Fi, p.Se); x2 = p.Fi, y2 = p.Se;
}
}else
{
cout << "WHITE" << endl;
vector<pii> v; int x = n / 2, y = m / 2;
while(x != x1 || y != y1) {
v.push_back({x, y}); pii p = g1[x][y]; x = p.Fi, y = p.Se;
}
reverse(v.begin(), v.end());
for(auto p : v)
{
put(p.Fi, p.Se); if(p.Fi == n / 2 && p.Se == m / 2) return 0;
pii q = get(); if(check(p.Fi, p.Se, q.Fi, q.Se)) return put(q.Fi, q.Se), 0;
}
}
}
return 0;
}
参考文献:
[1] https://www.luogu.com.cn/article/dwwy1fks
题外话:
被本题解满篇的 \(\unicode{x2658}\) 和 \(\unicode{x265E}\) 震撼到了吗?
如果没有,那肯定被代码的弱智大分讨震撼到了吧。(虽然我抄的是参考文献的代码)