进阶数学算法-取石子游戏(ZJOI2009)

目录

题目-取石子游戏

问题分析

用 l ( i , j ) l(i, j) l(i,j)表示对于当前情况, 左边放多少个石子先手必败

必然存在 并且必然唯一 , 第一个可以用递推的方式计算出, 对于唯一性, 以下是证明过程

证明:

假设对于某种局面有两个取值 L 1 L_1 L1, L 2 L_2 L2, 不妨设 L 1 < L 2 L_1 < L_2 L1<L2, 假设左边放了 L 1 L_1 L1个, 那么当前状态就是必败态

拿走 L 1 − L 2 L_1 - L_2 L1−L2个石子, 剩余左边的式子个数是 L 2 L_2 L2, 还是必败态

也就是说必败态转移到了必败态 , 矛盾

因此不存在 L 1 < L 2 L_1 < L_2 L1<L2的情况 , 同理不存在 L 1 > L 2 L_1 > L_2 L1>L2的情况

因此 L L L的取值只有一种, 证毕

同理给出 r ( i , j ) r(i, j) r(i,j), 假设假设计算出了 l ( i , j ) l(i, j) l(i,j)的所有取值

那么当 a 1 = l ( 2 , n ) a_1= l(2, n) a1=l(2,n)的时候先手必败

设 x = a j x = a_j x=aj具体来说如何计算 l ( i , j ) l(i, j) l(i,j)和 r ( i , j ) r(i, j) r(i,j), 因为 l l l和 r r r是对称的, 先考虑 l ( i , j ) l(i, j) l(i,j)如何计算

给出如下定义

  • 定义 L L L为左边放多少加上 i , j − 1 i, j - 1 i,j−1是必败态
  • 定义 R R R为右边放多少加上 i , j − 1 i, j - 1 i,j−1是必败态

分情况讨论

情况一

x = R x = R x=R, 那么当前已经是必败态了
l ( i , j ) = 0 l(i, j) = 0 l(i,j)=0

整体就是必败态

情况二

x < L , x < R x < L, x < R x<L,x<R l ( i , j ) = x l(i, j) = x l(i,j)=x

先手必败

策略: 后手拿取相同的个数 , 因为后手取完之后给先手的情况左右两边是相同的 , 并且石子个数是有限的, 必然会先手取完某一边变为 0 0 0个石子 , 另外一边还有石子个数是 y y y个

因为 x < L , x < R x < L, x < R x<L,x<R, 因此 y < L , y < R y < L, y < R y<L,y<R

因为必败态是唯一的, 要么左边添加 L L L个石子右边剩余 0 0 0个石子, 要么右边添加 R R R个石子左边剩余 0 0 0个石子

但是当前剩余的石子个数是 y y y, 因此左边是 y y y或者右边是 y y y都不是必败态 , 也就是留给对手的局面一定是必胜态, 证毕

情况三

L > R L > R L>R, 也就是 R < x < L R < x < L R<x<L

左边取 x − 1 x - 1 x−1, 也就是
l ( i , j ) = x − 1 l(i, j) = x - 1 l(i,j)=x−1

先手必败

首先先手不可能将右侧的 R R R拿完, 拿完就是必败态

假设先手拿左边的一堆, 左边剩余了 z z z个石子

如果 z ≥ R z \ge R z≥R, 后手将最右堆拿成 z + 1 z + 1 z+1状态(为了保证 R ′ R' R′始终小于 L ′ L' L′), 还是当前状态, 递归处理

直到 z < R z < R z<R也就是 z < L z < L z<L, 变成了情况二

假设先手拿右边的一堆, 右边剩余了 z z z个石子

如果 z ≥ R z \ge R z≥R, 左边拿 z − 1 z - 1 z−1个石子, 回到了当前情况, 递归处理

直到 z < R z < R z<R, 回到情况二

情况四

L < R L < R L<R, 也就是 L < x ≤ R L < x \le R L<x≤R

左边取 x + 1 x + 1 x+1, 也就是
l ( i , j ) = x + 1 l(i, j) = x + 1 l(i,j)=x+1

先手必败

证明与情况三类似, 略

情况五

x > L , x > R x > L, x > R x>L,x>R

左边取 x x x, 也就是
l ( i , j ) = x l(i, j) = x l(i,j)=x

先手必败

设先手将其中一堆拿成了 z z z个石子情况, 假设 z ≥ max ⁡ ( L , R ) z \ge \max(L, R) z≥max(L,R), 就是当前情况, 递归处理

直到 z < min ⁡ ( L , R ) z < \min(L, R) z<min(L,R), 是情况二, l ( i , j ) = x l(i, j) = x l(i,j)=x

算法步骤

计算 l ( i , j ) l(i, j) l(i,j)和 r ( i , j ) r(i, j) r(i,j)

因为当前状态依赖前一段 状态, 因此可以区间 d p dp dp解决

算法时间复杂度 O ( n 2 ) O(n ^ 2) O(n2)

代码实现

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;

const int N = 1010;

int n;
int a[N];
int l[N][N], r[N][N];

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		for (int i = 1; i <= n; i++) scanf("%d", &a[i]);

		for (int len = 1; len <= n; len++)
			for (int i = 1; i + len - 1 <= n; i++) {
				int j = i + len - 1;
				if (len == 1) l[i][j] = r[i][j] = a[i];
				else {
					int L = l[i][j - 1], R = r[i][j - 1], X = a[j];
					if (R == X) l[i][j] = 0;
					else if (X < L && X < R || X > L && X > R) l[i][j] = X;
					else if (L > R) 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 < L && X < R || X > L && X > R) r[i][j] = X;
					else if (R > L) r[i][j] = X - 1;
					else r[i][j] = X + 1;
				}
			}

		if (n == 1) puts("1");
		else printf("%d\n", l[2][n] != a[1]);
	}

	return 0;

}
相关推荐
裕晟资质规划7 小时前
武器装备科研生产单位保密资质申请方法论:条件模型与流程拆解
人工智能·算法
en.en..9 小时前
C语言 标准输入 / 输出缓冲区
算法
吃好睡好便好10 小时前
查找函数的使用
学习·算法·matlab·生活·查找函数
学习星球10 小时前
单调栈——从“找下一个更大的“到柱状图中的最大矩形
数据库·c++·算法·leetcode·xcode
靠沿12 小时前
贪心算法专题(二)
算法·贪心算法
猎头南楼12 小时前
具身智能模型部署方向的能力要求与技术栈梳理
人工智能·深度学习·算法
啥都想学点的研究生13 小时前
一篇文章讲清楚:线性回归问题的求解——正规方程法
算法·机器学习·线性回归
青少儿编程课堂13 小时前
树状数组精讲:逆序对计数与离散化(附双语言解法)
算法·树状数组·离散化·信息学奥赛·逆序对
血小板要健康14 小时前
网格 dfs 与 FloodFill:从岛屿、区域到搜索路径
笔记·算法·leetcode·深度优先
VALENIAN瓦伦尼安教学设备14 小时前
设备状态检测振动分析实训台案例分析
大数据·数据库·人工智能·嵌入式硬件·算法