[除草]BZOJ 1413 [ZJOI2009]取石子游戏

题目大意:

从左到右N堆石子, 每对石子数为a[i]. 两方轮流操作, 一次操作可以从最左边或最右边的石子堆取任意数量石子, 但不能不取, 不能操作的输. 问先手是否有必胜策略. (N不超过1000)

 

简要分析:

好囧的博弈题...现在我的脑袋里还是一团乱麻...

在尝试SG函数, 区间DP无果后, 在Discuss的诱导下注意到对于一段区间[L, R], 若L+1到R的石子数固定, 那么使得在这段区间上先手必败的a[L]有且仅有一个.

这个性质灰常给力啊. 于是可以YY一个状态出来. 设left[i][j]表示, 在[i, j]区间的左边加上left[i][j]这个数后先手必败, right[i][j]的定义类似. 那么最后我们只用看left[2][n]是否等于a[1]就可以了.

接着我们想办法来算left[i][j]. right[i][j]可以类似的求出.

设L = left[i][j - 1], R = right[i][j - 1], X = a[j]. 通过下面的分析我们可以发现left[i][j]只和L, R, X三个数有关.

首先, 最容易想到的是, R = X的情况, 这时[i, j]这段区间已经先手必败, 那么left[i][j] = 0.

接着, 我们可以发选当X < L且X < R时, left[i][j] = X. 在这种局面下, 若先手在一侧取走一些石子, 那么后手在另外一边取走相同数量的石子就可以了.

然后我们根据L和R的关系分类讨论一下.

若L > R, 我们考虑R < x <= L的情况, 这时left[i][j] = X - 1. X - 1 = R时是很轻松的, 因为先手不能把右侧石堆取到R, 所以后手保证每次取之后两堆石子相同就可以了. 当X - 1 > R时, 若先手把左边取到R, 那么后手把右边取到R + 1就可以了; 若先手取到R + 1, 那么后手取到R + 2; 以此类推.

若L < R, 我们考虑L <= X < R的情况, 这时left[i][j] = X + 1. 这个和上面类似.

最后的一种情况, x > L且x > R. 其实left[i][j] = X. 若L = R, 没啥说的; 若L和R不等, 我们不妨设L > R, 这时若先手把右边取到L, 那么后手需要把左边取到L - 1, 这时如果先手跟着后手走, 那么后手一颗一颗石子取就赢了; 若先手把左边取到R, 那么后手需要把右边取到R + 1, 这种情况似乎一定成立, 因为后手不会主动走到R + 1, 除非对方走到了R.

反正这个分析无比蛋疼...首先状态的定义非常奇葩, 具有一定的启发性(因为这题灰常隐蔽的一个性质)...然后分情况讨论无比痛苦...考场上还是找规律吧...

分类讨论的核心在于要找出后手的必胜策略.

 

代码实现:

View Code
 1 #include <cstdio>
 2 
 3 const int kMaxN = 1000;
 4 int t, n, s[kMaxN], fl[kMaxN][kMaxN], fr[kMaxN][kMaxN];
 5 
 6 int main() {
 7     scanf("%d", &t);
 8     while (t --) {
 9         scanf("%d", &n);
10         for (int i = 0; i < n; ++ i) scanf("%d", &s[i]);
11         for (int i = 0; i < n; ++ i) fl[i][i] = fr[i][i] = s[i];
12         for (int k = 1; k < n; ++ k)
13             for (int i = 0; i < n - k; ++ i) {
14                 int j = i + k, p, q, x;
15                 
16                 p = fl[i][j - 1], q = fr[i][j - 1], x = s[j];
17                 if (x == q) fl[i][j] = 0;
18                 else if ((x < p && x < q) || (x > p && x > q)) fl[i][j] = x;
19                 else if (p < q) fl[i][j] = x + 1;
20                 else fl[i][j] = x - 1;
21 
22                 p = fr[i + 1][j], q = fl[i + 1][j], x = s[i];
23                 if (x == q) fr[i][j] = 0;
24                 else if ((x < p && x < q) || (x > p && x > q)) fr[i][j] = x;
25                 else if (p < q) fr[i][j] = x + 1;
26                 else fr[i][j] = x - 1;
27             }
28         if (n == 1) printf("1\n");
29         else printf("%d\n", (fr[0][n - 2] != s[n - 1]));
30     }
31     return 0;
32 }
posted @ 2012-05-26 23:44  zcwwzdjn  阅读(2434)  评论(0编辑  收藏  举报