转自PoPoQQQ大佬博客

题目大意:给定n堆石子,两人轮流操作,每个人可以合并两堆石子或拿走一个石子,不能操作者输,问是否先手必胜

直接想很难搞,我们不妨来考虑一个特殊情况

假设每堆石子的数量都>1

那么我们定义操作数b为当前石子总数+当前堆数-1

若b为奇数,则先手必胜,否则后手必胜

证明:

若当前只有一堆,则正确性显然

否则:

若b为奇数,那么先手只需进行一次合成操作,此时操作数会-1,且仍不存在大小为1的堆

因此只需要证明b为偶数时先手必败即可

若先手选择了合成操作,那么操作数-1且不存在大小为1的堆,状态回到了b为奇数的状态

若先手取走了某个大小>=3的堆中的一个石子,那么操作数-1且不存在大小为1的堆,状态回到了b为奇数的状态

若先手取走了某个大小为2的堆中的一个石子,那么后手只需要将另一个石子与其它堆合成,b的奇偶性不变且仍不存在大小为1的堆

故b为偶数时先手必败

现在回到一般情况 可能存在大小为1的堆

我们设有a个大小为1的堆,其余堆的操作数为b

那么当前的状态就可以用一个二元组(a,b)来表示

容易发现a<=50,b<=50049

于是枚举每种操作暴力记忆化搜索即可

CODE(BZOJ)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 55;
const int MAXM = 50050;
int T, n, x;
char SG[MAXN][MAXM]; char ser(int a, int b) {
if(a == 0) return b&1;
//当不存在大小为1的堆时按照操作数计算必胜或必败
if(b == 1) return ser(a+1, 0);
//若操作数为1则此时b部分只有1个石子 划到a中
if(~SG[a][b]) return SG[a][b];
char &res = SG[a][b];
if(a && !ser(a-1, b)) return res = true;
//取走某个大小为1的堆中的石子
if(a && b && !ser(a-1, b+1)) return res = true;
//将某个大小为1的堆中的石子与某个大小不为1的堆合并
if(a > 1 && !ser(a-2, b+2+(b?1:0))) return res = true;
//将两个大小为1的堆中石子合并
if(b && !ser(a, b-1)) return res = true;
//对大小>1的堆进行合并或取走石子使操作数-1
return res = false;
} int main () {
memset(SG, -1, sizeof SG);
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
int A = 0, B = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d", &x);
if(x == 1) ++A;
else B += x+(B?1:0);
}
puts(ser(A, B) ? "YES" : "NO");
}
}

CODE(Luogu)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 55;
const int MAXM = 50050;
int T, n, x;
char SG[MAXN][MAXM]; char ser(int a, int b) {
if(a == 0) return b&1;
//当不存在大小为1的堆时按照操作数计算必胜或必败
if(b == 1) return ser(a+1, 0);
//若操作数为1则此时b部分只有1个石子 划到a中
if(~SG[a][b]) return SG[a][b];
char &res = SG[a][b];
if(a && !ser(a-1, b)) return res = true;
//取走某个大小为1的堆中的石子
if(a && b && !ser(a-1, b+1)) return res = true;
//将某个大小为1的堆中的石子与某个大小不为1的堆合并
if(a > 1 && !ser(a-2, b+2+(b?1:0))) return res = true;
//将两个大小为1的堆中石子合并
if(b && !ser(a, b-1)) return res = true;
//对大小>1的堆进行合并或取走石子使操作数-1
return res = false;
} int main () {
memset(SG, -1, sizeof SG);
scanf("%d", &T); int kase = 0;
while(T--) {
scanf("%d", &n);
int A = 0, B = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d", &x);
if(x == 1) ++A;
else B += x+(B?1:0);
}
printf("Case #%d: ", ++kase);
puts(ser(A, B) ? "Alice" : "Bob");
}
}

之所以用char存是因为只可能有-1/0/1三个值

还有对于b的其中某一堆石子可能从2取成1的情况的解释,见PoPoQQQ大佬博客评论区

BZOJ 3895 3895: 取石子 / Luogu SP9934 ALICE - Alice and Bob (博弈 记忆化搜索)的更多相关文章

  1. UVaLive 5760 Alice and Bob (博弈 + 记忆化搜索)

    题意:有 n 堆石子,有两种操作,一种是从一堆中拿走一个,另一种是把两堆合并起来,Alice 先拿,谁不能拿了谁输,问谁胜. 析:某些堆石子数量为 1 是特殊,石子数量大于 1 个的都合并起来,再拿, ...

  2. HDU 4111 Alice and Bob (博弈+记忆化搜索)

    题意:给定 n 堆石头,然后有两种操作,一种是把从任意一堆拿走一个,另一种是把一个石子放到另一堆上. 析:整体看,这个题真是不好做,dp[a][b] 表示有 a 堆1个石子,b个操作,操作是指把其他的 ...

  3. luogu P2657 [SCOI2009]windy数 数位dp 记忆化搜索

    题目链接 luogu P2657 [SCOI2009]windy数 题解 我有了一种所有数位dp都能用记忆话搜索水的错觉 代码 #include<cstdio> #include<a ...

  4. bzoj 1668: [Usaco2006 Oct]Cow Pie Treasures 馅饼里的财富【记忆化搜索+剪枝】

    c[x][y]为从(x,y)到(n,m)的最大值,记忆化一下 有个剪枝是因为y只能+1所以当n-x>m-y时就算x也一直+1也是走不到(n,m)的,直接返回0即可 #include<ios ...

  5. 【BZOJ-3895】取石子 记忆化搜索 + 博弈

    3895: 取石子 Time Limit: 1 Sec  Memory Limit: 512 MBSubmit: 263  Solved: 127[Submit][Status][Discuss] D ...

  6. bzoj3895: 取石子(博弈论,记忆化搜索)

    3895: 取石子 Time Limit: 1 Sec  Memory Limit: 512 MBSubmit: 361  Solved: 177[Submit][Status][Discuss] D ...

  7. 【BZOJ3895】取石子(博弈,记忆化搜索)

    题意: Alice和Bob两个好朋含友又开始玩取石子了.游戏开始时,有N堆石子排成一排,然后他们轮流操作(Alice先手),每次操作时从下面的规则中任选一个:1:从某堆石子中取走一个2:合并任意两堆石 ...

  8. HDU.2516 取石子游戏 (博弈论 斐波那契博弈)

    HDU.2516 取石子游戏 (博弈论 斐波那契博弈) 题意分析 简单的斐波那契博弈 博弈论快速入门 代码总览 #include <bits/stdc++.h> #define nmax ...

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

    取石子游戏 HDU 1527 博弈论 威佐夫博弈 题意 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两 ...

随机推荐

  1. kafka producer 生产者客户端参数配置

    在生产者向broker发送消息时,需要配置不同的参数来确保发送成功. acks = all #指定分区中有多少副本必须收到这条消息,生产者才认为这条消息发送成功 acks = 0 #生产者发送消息之后 ...

  2. Scala当中什么是Transformation和 Action,以及它们俩的区别是什么?

    [学习笔记] 一个完整的RDD任务由两部分组成:Transformation和 Action.Transformation用于对RDD的创建,还可以把老的RDD通过Transformation来生成新 ...

  3. 手机网站支付如何接入支付宝简易版支付功能PHP版

    接入支付宝准备工作:(关于账号可以是个体商户也可以是企业账号但必须有营业执照) 1.登录蚂蚁金服开放平台  2.创建应用,应用分类网页应用和移动应用.应用提交审核审核通过后得到Appid才能调用相应的 ...

  4. idea中创建的go项目,添加project sdk时没有go sdk选项的解决方式

    同样是后端开发,年薪50万和年薪20万的差距在哪里>>> 更新: 为了防止你被我这个流水账气到,先看这个结论吧:这个问题的结局方法:忽略,没有什么影响. -------------- ...

  5. element-ui当中table组件的合并行和列的属性:span-method的用法

    背景 最近基本上都是以Vue来构建项目,而UI框架也基本上都是使用的element-ui,所以里面组件用的也是越来越多,今天想记录的是非常非常小的一个属性的用法. Table组件 Table组件用了真 ...

  6. hdu 2610 2611 dfs的判重技巧

    对于全排列枚举的数列的判重技巧 1:如果查找的是第一个元素 那么 从0开始到当前的位置看有没有出现过这个元素 出现过就pass 2: 如果查找的不是第一个元素 那么 从查找的子序列当前位置的前一个元素 ...

  7. SpringBoot事务隔离等级和传播行为

    一.开启事物管理 //import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBoo ...

  8. springboot内置tomcat配置虚拟路径

    在Springboot中默认的静态资源路径有:classpath:/METAINF/resources/,classpath:/resources/,classpath:/static/,classp ...

  9. Nginx用法详解

    nginx作为一个高性能的web服务器,想必大家垂涎已久,蠢蠢欲动,想学习一番了吧,语法不多说,网上一大堆.下面博主就nginx的非常常用的几个功能做一些讲述和分析,学会了这几个功能,平常的开发和部署 ...

  10. ‘mysql’不是内部或外部命令,也不是可运行的程序--解决方法

    一.场景 在cmd命令窗口下操作mysql时,提示mysql不是内部或外部命令,也不是可运行的程序. 二.原因 有3种原因: 1.没有装mysql 2.没有配置mysql环境变量 3.cmd命令窗口没 ...