题解 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\)。故最终得到:

\[L(i, j)=\begin{cases} 1, &a_j<R(i + 1, j),\\ L(i, j - 1)+a_j-R(i + 1, j)+1, &\text{otherwise}. \end{cases} \]

\(R(i, j)\) 应该是镜像对称的。

\[R(i, j)=\begin{cases} 1, &a_i<L(i, j - 1),\\ R(i+1, j)+a_i-L(i, j - 1)+1, &\text{otherwise}. \end{cases} \]

边界是先手必胜的单点。\(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;
}
posted @ 2024-07-11 17:20  caijianhong  阅读(13)  评论(0编辑  收藏  举报