BZOJ 1413. [ZJOI2009]取石子游戏
用dp来解决博弈问题这是第一次做,之前遇到一个博弈dp的题还没补。。
设 $L_{i,j},R_{i,j}$ 分别表示在区间 $[i,j]$ 左右放上多少石子能让先手必败。
首先这个 $L,R$ 肯定是唯一的,假设不唯一,即存在 $L_1,L_2$ 加在当前区间左端能使先手必败,设 $L_1>L_2$,那么先手把 $L_1$ 取得和 $L_2$ 一样就能让后手必败,与必败态的定义不符,所以是唯一的。
$L_{i,i}=R_{i,i}=a_i$,因为有两堆相同的石子的时候,后手复制先手的操作即可。
现在已知 $L_{i,j-1},R_{i,j-1},a_j$,求 $L_{i,j}$。
当 $R_{i,j-1}=a_j$ 时,$L_{i,j}=0$,因为当前已经是必败态了,所以左边不用加一堆石子了。
当 $a_j < L_{i,j-1} \wedge a_j < R_{i,j-1}$ 时,$L_{i,j}=a_j$,后手复制先手的操作即可,当先手取完一堆石子之后,相当于先手从 $L_{i,j-1}$ 或 $R_{i,j-1}$ 取了一次石子,必败态转为必胜态。
当 $R_{i,j-1} < a_j \leq L_{i,j-1}$ 时,$L_{i,j}=a_j-1$,先手若把右端取成 $R_{i,j-1}$,那么后手把左边全取光就必胜了,若先手取得使右端比 $R_{i,j-1}$ 多,那么后手和他取一样多的石子维持上述状态,若先手取得使右端比 $R_{i,j-1}$ 少,那么后手把石子取成和他一样多,就是上面第二种情况了。若先手先取左端,左端若大于等于 $R_{i,j-1}$, 那么后手取一样多维持状态,否则取到和左端一样转成第二种情况。
当 $L_{i,j-1} < a_j \leq R_{i,j-1}$ 时,$L_{i,j}=a_j+1$,分析方法如上。
若 $a_j > L_{i,j-1} \wedge a_j > R_{i,j-1}$,$L_{i,j}=a_j$,取法相同,一直取左右保持一致,当左边等于 $L_{i,j-1}$ 或右边等于 $R_{i,j-1}$,另一边比它多一即可。
最后判断 $R_{1,n-1}$ 是否等于 $a_n$ 就能得到答案。
#include <bits/stdc++.h> const int N = 1e3 + 7; int a[N], l[N][N], r[N][N], n; void solve() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", a + i), l[i][i] = r[i][i] = a[i]; for (int len = 2; len <= n; len++) { for (int i = 1; i + len - 1 <= n; i++) { int j = i + len - 1; int L = l[i][j - 1], R = r[i][j - 1], x = a[j]; if (R == x) l[i][j] = 0; else if ((x < R && x < L) || (x > R && x > L)) l[i][j] = x; else if (R < L) l[i][j] = x - 1; else l[i][j] = x + 1; L = l[i + 1][j], R = r[i + 1][j], x = a[i]; if (L == x) r[i][j] = 0; else if ((x < R && x < L) || (x > R && x > L)) r[i][j] = x; else if (L < R) r[i][j] = x - 1; else r[i][j] = x + 1; } } if (r[1][n - 1] == a[n]) puts("0"); else puts("1"); } int main() { int T; scanf("%d", &T); while (T--) solve(); return 0; }