P2599 [ZJOI2009]取石子游戏

题目描述

在研究过Nim游戏及各种变种之后,Orez又发现了一种全新的取石子游戏,这个游戏是这样的: 有n堆石子,将这n堆石子摆成一排。游戏由两个人进行,两人轮流操作,每次操作者都可以从最左或最右的一堆中取出若干颗石子,可以将那一堆全部取掉,但不能不取,不能操作的人就输了。 Orez问:对于任意给出一个初始一个局面,是否存在先手必胜策略。

输入格式

文件的第一行为一个整数T,表示有 T组测试数据。对于每组测试数据,第一行为一个整数n,表示有n堆石子;第二行为n个整数ai,依次表示每堆石子的数目。

输出格式

对于每组测试数据仅输出一个整数0或1。其中1表示有先手必胜策略,0表示没有。

输入输出样例

输入 #1

1

4

3 1 9 4

输出 #1

0

说明/提示

数据范围

对于30%的数据 n≤5 ai≤10^5

对于100%的数据 T≤10 n≤1000 每堆的石子数目≤10^9

思路

By  :yybyyb

发现SG函数等东西完全找不到规律,无奈只能翻题解。

首先设L[i][j]表示在[i,j]这一段区间的左侧放上一堆数量为L[i][j]的石子后,先手必败。同理定义R[i][j]表示右侧。

首先我们可以证明L[i][j]唯一,假设存在两个L[i][j],显然较大的那个可以通过一步转移转移到较小的那个,所以不合法。因此L[i][j]唯一。

接下来考虑如何证明L[i][j]一定存在。假设L[i][j]不存在,那么对于这段区间而言,在左边加上任意一堆石子先手都必胜,既然先手必胜意味着先手进行一步操作之后可以到达一个必败态,这里分情况讨论。假设先手拿的是最左边的一堆石子,因为不存在L[i][j],所以只要拿了左边的石子之后,当前局面都是必胜态,所以不可能拿左边的石子。那么只能拿右边的石子,那么无论右边拿了一定量之后,无论左边添加了多少,都是一个必败态,那么此时后手在左侧随便拿走一定数量,这个状态也还是一个必败态,显然也不成立。因此L[i][j]必定存在。

综上,我们知道了L[i][j]一定存在并且唯一,而L[][],R[][]显然是对称的,因此R[i][j]也满足上述性质。

现在考虑如何求解L[i][j],R[i][j]同理。首先边界情况显然,L[i][i]=a[i],因为只剩下两堆一模一样的情况的时候,后手只需要模仿先手的行动对称执行就好了,这样子一定不会输,即先手必败。

接下来来大力分类讨论,为了方便,设L=L[i][j−1],R=R[i][j−1],x=a[j]

  • x=R

    这种情况下显然只需要直接把a[j]放进去就好了,即这个区间本身就是一个必败态。所以L[i][j]=0。

  • x<L,x<R

    这种情况下L[i][j]=x。这种情况下最靠左的L[i][j]和x=a[j]是相同的,意味着先手无论怎么取,后手显然可以学着它的方法取,也就意味着左右两堆中显然必然会先拿完一堆,此时后手学着拿的那一堆的石子数一定也是小于L,R的。假设先手先拿完了最靠右的一堆,即剩下了[i,j−1],因为L[i][j−1]表示的是在这一段区间最左侧加入一个L[i][j−1的堆,无论先手怎么取先手都是必败的,那么我们等价的认为先手取走了这一堆的一部分,显然后手是必胜的。假如先手先取完的是最左的一堆,同理,R[i][j−1]的含义是在最右侧加入了一堆,而a[j]<R[i][j−1],我们还是可以等价的认为先手在这一堆中取走了若干石子,而这个状态对于先手而言是必败状态,因此显然后手必胜。

  • R<x<L

    这种情况下L[i][j]=x−1。这样子考虑,假设先手先拿了左边这一堆,那么假设还剩下了z个石子,如果z<R,后手把右侧的那一堆也给拿成z就变成了上面的情况。如果z≥R,那么后手把最后那一堆拿成z+1,于是又回到了这种情况,相当于这种情况递归处理。如果先手先拿的是右侧的这一堆,还是一样的,假设把它拿成了z,如果z<R,同上可以变成x<L,x<R的情况;如果y=R,直接把左边拿完,就变成了R[i][j−1]的定义了,先手必败;如果z>R,把左边那堆变成z−1,同样递归处理。

  • L<x<R

    分析同上,L[i][j]=x+1。

  • x>L,x>R

    L[i][j]=x。还是一样的,假设先手把其中一堆拿成了z。如果z>L,R,跟着先手拿成一样多的石子则又回到了这种情况。如果z<L,R,则可以回到情况x<L,R。否则的话对应着把另外一堆变成z+1或者z−1,对应着L<x<R和R<x<L两种情况。

而R[][]和L[][]是对称的,类似的求解即可。

那么最终只需要判断L[2][n]和a[1]是否相等即可判断胜负情况。

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int N=1010;

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

int main () {
	scanf("%d",&t);
	while(t--) {
		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,j=i+len-1; j<=n; i++,j++) {
				int x=a[j],ll=l[i][j-1],rr=r[i][j-1];
				if(x==rr)
					l[i][j]=0;
				else if((x>rr&&x>ll)||(x<ll&&x<rr))
					l[i][j]=x;
				else if(rr<x&&x<ll)
					l[i][j]=x-1;
				else
					l[i][j]=x+1;
				x=a[i],ll=l[i+1][j],rr=r[i+1][j];
				if(x==ll)
					r[i][j]=0;
				else if((x>rr&&x>ll)||(x<ll&&x<rr))
					r[i][j]=x;
				else if(rr<x&&x<ll)
					r[i][j]=x+1;
				else
					r[i][j]=x-1;
			}
		if(a[1]==l[2][n])
			printf("0\n");
		else
			printf("1\n");
	}
	return 0;
}

原文地址:https://www.cnblogs.com/mysh/p/11826050.html

时间: 2024-10-28 10:16:12

P2599 [ZJOI2009]取石子游戏的相关文章

bzoj1413 [ZJOI2009]取石子游戏

Description 在研究过Nim游戏及各种变种之后,Orez又发现了一种全新的取石子游戏,这个游戏是这样的: 有n堆石子,将这n堆石子摆成一排.游戏由两个人进行,两人轮流操作,每次操作者都可以从最左或最右的一堆中取出若干颗石子,可以将那一堆全部取掉,但不能不取,不能操作的人就输了. Orez问:对于任意给出一个初始一个局面,是否存在先手必胜策略. Input 文件的第一行为一个整数T,表示有 T组测试数据.对于每组测试数据,第一行为一个整数n,表示有n堆石子:第二行为n个整数ai,依次表示

[ZJOI2009]取石子游戏

瞪了题解两三天,直接下转第二篇题解就康懂了 首先我们令 : \(L[i][j]\) 表示当前 \([i,j]\) 区间左侧放置 \(L[i,j]\) 数量的石子后先手必败 \(R[i][j]\) 表示当前 \([i,j]\) 区间右侧放置 \(R[i,j]\) 数量的石子后先手必败 那么最后我们只要判断 \(a[1]\) 是否等于 \(L[2,n]\) 或者 \(a[n]\) 是否等于 \(R[1,n-1]\) 即可 唯一性 考虑证明 \(L[i][j]\) 和 \(R[i][j]\) 的唯一性

poj 1067||hdu 1527 取石子游戏(博弈论,Wythoff Game)

取石子游戏 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 37893   Accepted: 12684 Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同数量的石子.最后把石子全部取完者为胜者.现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者

HDU 1527 取石子游戏 威佐夫博弈

题目来源:HDU 1527 取石子游戏 题意:中文 思路:威佐夫博弈 必败态为 (a,b ) ai + i = bi     ai = i*(1+sqrt(5.0)+1)/2   这题就求出i然后带人i和i+1判断是否成立 以下转自网上某总结 有公式ak =[k(1+√5)/2],bk= ak + k  (k=0,1,2,-,n 方括号表示取整函数) 其中出现了黄金分割数(1+√5)/2 = 1.618-,因此,由ak,bk组成的矩形近似为黄金矩形 由于2/(1+√5)=(√5-1)/2,可以先

BZOJ 1874: [BeiJing2009 WinterCamp]取石子游戏 [Nim游戏 SG函数]

小H和小Z正在玩一个取石子游戏. 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子,每次取石子的个数有限制,谁不能取石子时就会输掉游戏. 小H先进行操作,他想问你他是否有必胜策略,如果有,第一步如何取石子. N≤10 Ai≤1000 裸SG函数啊 然而我连SG函数都不会求了,WA了一会儿之后照别人代码改发现vis公用了... #include <iostream> #include <cstdio> #include <cstring> #includ

hdu 2516 取石子游戏

取石子游戏 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2816    Accepted Submission(s): 1626 Problem Description 1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍.取完者胜.先取者负输出"Second win&qu

BZOJ 1413 取石子游戏(DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1413 题意:n堆石子排成一排.每次只能在两侧的两堆中选择一堆拿.至少拿一个.谁不能操作谁输. 思路:参考这里. int f1[N][N],f2[N][N],n,a[N]; void deal() { RD(n); int i,j,k; FOR1(i,n) RD(a[i]),f1[i][i]=f2[i][i]=a[i]; int p,q,x; for(k=2;k<=n;k++) for(

POJ - 1067 取石子游戏(包括贝蒂定理的巧妙证明)

关键词: 取石子游戏.威佐夫博奕.betty贝蒂定理.胜态.负态.状态转移.覆盖(分划).高斯函数.第二数学归纳法.黄金分割比例 题目: Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同数量的石子.最后把石子全部取完者为胜者.现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者. Input 输入包含若干行,表示若干种石

HDU 2516 取石子游戏 (博弈论)

取石子游戏 Problem Description 1堆石子有n个,两人轮流取.先取者第1次能够取随意多个,但不能所有取完.以后每次取的石子数不能超过上次取子数的2倍.取完者胜.先取者负输出"Second win".先取者胜输出"First win". Input 输入有多组.每组第1行是2<=n<2^31. n=0退出. Output 先取者负输出"Second win". 先取者胜输出"First win".