题解 AGC048D【Pocky Game】
题目描述
现在有\(N\)堆石子,第\(i\)石子有\(a_i\)个,现在有两个人(Firstleft和SecondRight)玩这个游戏,Firstleft先手。
每一轮,Firstleft可以从最左边一个至少有一颗石子的堆中拿走至少一颗(最多拿完),然后,SecondRight可以从最右边一个至少有一颗石子的堆中拿走至少一颗(最多拿完)。问先手有无必胜策略。
- $ 1\ \leq\ T\ \leq\ 100 $
- $ 1\ \leq\ N\ \leq\ 100 $
- $ 1\ \leq\ A_i\ \leq\ 10^9 $
solution
区间 dp。
先手,如果左边有一堆无限高的石子,那么先手一个一个取,直到后手逼到左边,先手一次性再取完,先手必胜。
发现两人的决策,要么取一个,要么全取,否则感性理解一下会亏轮次。
\(L(i, j)\) 表示 \(a_i\geq L(i, j)\) 时先手必胜,否则必败。\(R(i, j)\) 表示 \(a_j\geq R(i, j)\) 时后手必胜,否则必败。
考虑 \(L(i, j)\) 之转移。若先手全取 \(a_i\) 这一堆,则需要满足后手必败,\(a_j<R(i + 1, j)\)。否则,先手只取一个,然后考虑后手决策,后手要么是全取,进入 \(L(i, j - 1)\) 状态,后手仅在 \(a_i-1< L(i, j - 1)\) 时才敢全取。否则后手取一个,先手不妨一直和后手对线,让后手一直取,你后手要敢取到 \(a_j<R(i + 1, j)\) 先手就立即审判,先手全取,隐含要求是 \(a_j\geq R(i+1, j)\),先手得有 \(a_j-R(i + 1, j) + 1\) 个石子和后手消耗。我们还要慎防后手全取,过了 \(k(a_j-k\geq R(i + 1, j))\) 个回合(先后手各取一个)以后,如果后手全取了,就是它发现 \(a_i-k-1<L(i, j-1)\)。先手为了不能被后手薄纱,一定是 \(a_i-a_j+R(i+1, j)-1\geq L(i, j-1)\)。我们发现这玩意,就相当于到了对方的必败极限之后立马使对方必败,先手需要保证自己存活轮次比对方多,这个条件概括了从取一个开始的所有字。然后这里 \(a_i\) 实际上是 \(L(i, j)\) 代名词,得到了 \(L(i, j)= L(i, j - 1)+a_j-R(i + 1, j)+1\)。故最终得到:
\(R(i, j)\) 应该是镜像对称的。
边界是先手必胜的单点。\(O(n^2)\)。
code
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
typedef long long LL;
int n, a[110];
LL L[110][110], R[110][110];
int mian() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) L[i][i] = R[i][i] = 1;
for (int t = 2; t <= n; t++) {
for (int i = 1, j = t; j <= n; i++, j++) {
if (a[j] < R[i + 1][j]) L[i][j] = 1;
else L[i][j] = L[i][j - 1] + a[j] - R[i + 1][j] + 1;
if (a[i] < L[i][j - 1]) R[i][j] = 1;
else R[i][j] = R[i + 1][j] + a[i] - L[i][j - 1] + 1;
}
}
cout << (L[1][n] <= a[1] ? "First" : "Second") << endl;
return 0;
}
int main() {
#ifndef LOCAL
cin.tie(nullptr)->sync_with_stdio(false);
#endif
int t;
cin >> t;
while (t--) mian();
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/18296748/solution-AGC048D