E. Gojou and Matrix Game
这里我们定义一次(先手,后手)为一轮游戏,因为进行
1
0
100
10^{100}
10100 次,所以可以进行整数轮游戏。
首先我们可以发现如果先手站在最大值上直接就赢了,因为后手无论走哪个点,先手又可以回来取最大点,所以每一轮先手都可以比后手优,所以必胜。
然后我们考虑先手初始在非最大值的情况,我们可以发现,如果先手站在非最大值上,假设这个值是
v
v
v ,那么后手下一步必须走到
>
v
>v
>v 的点,为何呢,如果后手不这么做,选了一个
<
v
<v
<v 的点,先手下一次又可以选回
v
v
v ,本身我后手第一轮我就亏了,然后还回到了与上一轮一样的情况(先手选了
v
v
v ),说白了我白亏一轮,所以后手不可能白亏,所以他必定会选
>
v
>v
>v 的点。
那么我们发现,我们可以按
v
v
v 值倒序遍历,递推每个点的答案。这里我们设
f
[
x
]
[
y
]
f[x][y]
f[x][y] 表示初始站在
(
x
,
y
)
(x,y)
(x,y) 点的胜败态,
f
[
x
]
[
y
]
=
0
f[x][y]=0
f[x][y]=0 为败,
f
[
x
]
[
y
]
=
1
f[x][y]=1
f[x][y]=1 为胜。
那么我们已知
v
[
x
]
[
y
]
v[x][y]
v[x][y] 为
m
a
x
max
max 时,
f
[
x
]
[
y
]
=
1
f[x][y]=1
f[x][y]=1
假设当前遍历到
(
x
,
y
)
(x,y)
(x,y) ,那么后手只有到达点
(
x
1
,
y
1
)
(x_1,y_1)
(x1?,y1?) 满足
v
[
x
1
]
[
y
1
]
>
v
[
x
]
[
y
]
v[x_1][y_1]>v[x][y]
v[x1?][y1?]>v[x][y] 且
f
[
x
1
]
[
y
1
]
=
1
f[x_1][y_1]=1
f[x1?][y1?]=1 才能赢,因为我们是倒序遍历,所以比
v
[
x
]
[
y
]
v[x][y]
v[x][y] 值大的点,且是胜利态的点
(
x
1
,
y
1
)
(x_1,y_1)
(x1?,y1?) 我们是已经得到了的,那么
(
x
,
y
)
(x,y)
(x,y) 能走到
(
x
1
,
y
1
)
(x_1,y_1)
(x1?,y1?) 的条件是 :
∣
x
?
x
1
∣
+
∣
y
?
y
1
∣
>
k
|x-x_1|+|y-y_1|>k
∣x?x1?∣+∣y?y1?∣>k 那么只要存在一个满足上述条件的点
(
x
1
,
y
1
)
(x_1,y_1)
(x1?,y1?) 后手即可获胜,这里我们可以反过来求,如果所有
(
x
1
,
y
1
)
(x_1,y_1)
(x1?,y1?) 都满足:
∣
x
?
x
1
∣
+
∣
y
?
y
1
∣
≤
k
|x-x_1|+|y-y_1|\leq k
∣x?x1?∣+∣y?y1?∣≤k 那么即后手没有机会赢,先手必胜,反之先手必败。
那么我们将式子做一个转换:
∣
x
?
x
1
∣
+
∣
y
?
y
1
∣
=
m
a
x
(
∣
(
x
+
y
)
?
(
x
1
+
y
1
)
∣
,
∣
(
x
?
y
)
?
(
x
1
?
y
1
)
∣
)
|x-x_1|+|y-y_1|=max(|(x+y)-(x_1+y_1)|,|(x-y)-(x_1-y_1)|)
∣x?x1?∣+∣y?y1?∣=max(∣(x+y)?(x1?+y1?)∣,∣(x?y)?(x1??y1?)∣)
这里粗略的证明一下上述公式,
∣
x
?
x
1
∣
+
∣
y
?
y
1
∣
|x-x_1|+|y-y_1|
∣x?x1?∣+∣y?y1?∣ 把绝对值拆开只可能产生四个式子:
(
x
+
y
)
?
(
x
1
+
y
1
)
(x+y)-(x_1+y_1)
(x+y)?(x1?+y1?)
(
x
1
+
y
1
)
?
(
x
+
y
)
(x_1+y_1)-(x+y)
(x1?+y1?)?(x+y)
(
x
?
y
)
?
(
x
1
?
y
1
)
(x-y)-(x_1-y_1)
(x?y)?(x1??y1?)
(
x
1
?
y
1
)
?
(
x
?
y
)
(x_1-y_1)-(x-y)
(x1??y1?)?(x?y)
这里设
①
=
∣
x
?
x
1
∣
①=|x-x_1|
①=∣x?x1?∣ ,
②
=
∣
y
?
y
1
∣
②=|y-y_1|
②=∣y?y1?∣ ,
∣
x
?
x
1
∣
+
∣
y
?
y
1
∣
|x-x_1|+|y-y_1|
∣x?x1?∣+∣y?y1?∣ 正确的答案应该是
①
+
②
①+②
①+② ,而在上述把绝对值拆开的四个等式中倘若不是正确的值,则只可能是
①
①
① 或
②
②
② 取了负号,那么答案一定会变小,所以正确的值一定是上述四个式子中最大的一个。那么再将上述四个式子两两合并写一下就是:
∣
x
?
x
1
∣
+
∣
y
?
y
1
∣
=
m
a
x
(
∣
(
x
+
y
)
?
(
x
1
+
y
1
)
∣
,
∣
(
x
?
y
)
?
(
x
1
?
y
1
)
∣
)
|x-x_1|+|y-y_1|=max(|(x+y)-(x_1+y_1)|,|(x-y)-(x_1-y_1)|)
∣x?x1?∣+∣y?y1?∣=max(∣(x+y)?(x1?+y1?)∣,∣(x?y)?(x1??y1?)∣) 证毕
因此我们最终可以得到:
∣
x
?
x
1
∣
+
∣
y
?
y
1
∣
≤
k
??
?
??
m
a
x
(
∣
(
x
+
y
)
?
(
x
1
+
y
1
)
∣
,
∣
(
x
?
y
)
?
(
x
1
?
y
1
)
∣
)
≤
k
|x-x_1|+|y-y_1|\leq k \iff max(|(x+y)-(x_1+y_1)|,|(x-y)-(x_1-y_1)|) \leq k
∣x?x1?∣+∣y?y1?∣≤k?max(∣(x+y)?(x1?+y1?)∣,∣(x?y)?(x1??y1?)∣)≤k 所以我们只要维护
(
x
1
+
y
1
)
(x_1+y_1)
(x1?+y1?) 和
(
x
1
?
y
1
)
(x_1-y_1)
(x1??y1?) 分别的最大和最小值,这样我们就可以知道上述式子是否成立,若成立则
f
[
x
]
[
y
]
=
0
f[x][y]=0
f[x][y]=0 ,否则
f
[
x
]
[
y
]
=
1
f[x][y]=1
f[x][y]=1 。若
f
[
x
]
[
y
]
=
1
f[x][y]=1
f[x][y]=1 则需要更新最小和最大值。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
struct node {
int x;
int y;
int v;
bool operator < (const node &t) {
return v > t.v;
}
};
int n, k;
bool f[N][N];
vector<node> a;
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
int v; scanf("%d", &v);
a.push_back({i, j, v});
}
}
sort(a.begin(), a.end());
f[a[0].x][a[0].y] = 1;
int mn_xaddy, mn_xsuby, mx_xaddy, mx_xsuby;
mn_xaddy = mx_xaddy = a[0].x + a[0].y;
mn_xsuby = mx_xsuby = a[0].x - a[0].y;
for (int i = 1; i < n * n; ++i) {
int x = a[i].x, y = a[i].y;
if (max({abs(x+y-mn_xaddy), abs(x+y-mx_xaddy), abs(x-y-mn_xsuby), abs(x-y-mx_xsuby)}) <= k) {
f[x][y] = 1;
mn_xaddy = min(mn_xaddy, x+y);
mx_xaddy = max(mx_xaddy, x+y);
mn_xsuby = min(mn_xsuby, x-y);
mx_xsuby = max(mx_xsuby, x-y);
}
else {
f[x][y] = 0;
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
printf("%c", f[i][j] == 1 ? 'M' : 'G');
}
puts("");
}
}
|