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$ 就能得到答案。

BZOJ 1413. [ZJOI2009]取石子游戏
#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;
}
View Code

 

上一篇:JAVA猜年龄;


下一篇:暂存