2011 ACM-ICPC 成都赛区A题 Alice and Bob (博弈动规)

题目大意:

有K堆石子,每堆有Ki个。两人的操作能够是:

1 从某一堆拿走一个 假设该堆在此之后没有石子了。就消失

2 合并两个堆

求是否先手必胜,先手胜输出Alice。否则输出Bob

思路:

这道题读完后毫无头绪。推了半天也推不个所以然来,參看大神代码后,感觉就是一个记忆化搜索啊,唉,知识学多了不会用还是白搭。还得多做题啊!

这里我们把数字分成 1,2,大于等于3的奇数,大于等于4的偶数四类。

这样分的原因在于。一个大于3的奇数是实际上等价于3的;由于每当对手减一个。自己也减一个。就又变回了一个大于3的奇数。终于变成3。

同理。全部大于2的偶数等价于4。

所以我们用dp[a][b][c][d]表示有a个1,b个2,c个3,d个4是不是一个必胜态,然后动规求解就好了。

代码:

#include <stdio.h>
#define N 51

bool dp[N][N][N][N] = {0};
bool vis[N][N][N][N] = {0};

int F(int a, int b, int c, int d)							// 一个相似记忆化搜索的过程
{
	if(!vis[a][b][c][d]){									// 假设后继状态为P状态,则此状态为N状态
		if(a >= 1 && !F(a - 1, b, c, d)) dp[a][b][c][d] = 1;			// 从 a 中某堆拿走一个。消失
		if(b >= 1 && !F(a + 1, b - 1, c, d)) dp[a][b][c][d] = 1;		// 从 b 中某堆拿走一个。变成 a
		if(c >= 1 && !F(a, b + 1, c - 1, d)) dp[a][b][c][d] = 1;		// 从 c 中某堆拿走一个。变成 b
		if(d >= 1 && !F(a, b, c + 1, d - 1)) dp[a][b][c][d] = 1;		// 从 d 中某堆拿走一个,变成 c

		if(a >= 2 && !F(a - 2, b + 1, c, d)) dp[a][b][c][d] = 1;		// 合并 a 中的两堆,变成 b 类的一堆
		if(b >= 2 && !F(a, b - 2, c, d + 1)) dp[a][b][c][d] = 1;		// 合并 b 中的两堆,变成 d 类的一堆
		if(c >= 2 && !F(a, b, c - 2, d + 1)) dp[a][b][c][d] = 1;		// 合并 c 中的两堆,变成 d 类的一堆
		if(d >= 2 && !F(a, b, c, d - 2 + 1)) dp[a][b][c][d] = 1;		// 合并 d 中的两堆,变成 d 类的一堆

		if(a >= 1 && b >= 1 && !F(a - 1, b - 1, c + 1, d)) dp[a][b][c][d] = 1;		// 合并 a、b 中的一堆,变成 c 类的一堆
		if(a >= 1 && c >= 1 && !F(a - 1, b, c - 1, d + 1)) dp[a][b][c][d] = 1;		// 合并 a、c 中的一堆。变成 d 类的一堆
		if(a >= 1 && d >= 1 && !F(a - 1, b, c + 1, d - 1)) dp[a][b][c][d] = 1;		// 合并 a、d 中的一堆,变成 c 类的一堆
		if(b >= 1 && c >= 1 && !F(a, b - 1, c - 1 + 1, d)) dp[a][b][c][d] = 1;		// 合并 b、c 中的一堆,变成 c 类的一堆
		if(b >= 1 && d >= 1 && !F(a, b - 2, c, d - 1 + 1)) dp[a][b][c][d] = 1;		// 合并 b、d 中的一堆。变成 d 类的一堆
		if(c >= 1 && d >= 1 && !F(a, b, c - 1 + 1, d - 1)) dp[a][b][c][d] = 1;		// 合并 c、d 中的一堆,变成 c 类的一堆

		vis[a][b][c][d] = 1;
	}

	return dp[a][b][c][d];
}

int main()
{
	int loop, n, t, ct = 1;
	int a, b, c, d, ans;			// a 表示仅仅有一个石子的堆数,b 表示有仅仅有两个石子的堆数。c 表示有奇数个石子的堆数, d 表示有偶数个石子的堆数
	scanf("%d", &loop);
	while(ct <= loop){
		scanf("%d", &n);
		a = b = c = d = 0;
		for(int i = 0; i < n; i ++){
			scanf("%d", &t);
			if(t == 1) a ++;
			else if(t == 2)	b ++;
			else if(t % 2 == 1) c ++;
			else d ++;
		}

		ans = F(a, b, c, d);
		printf("Case #%d: ", ct ++);
		if(ans)
			printf("Alice\n");
		else
			printf("Bob\n");
	}

	return 0;
}
时间: 2024-11-08 21:20:53

2011 ACM-ICPC 成都赛区A题 Alice and Bob (博弈动规)的相关文章

ACM训练联盟周赛 C题 Alice和Bob的Nim游戏

题目描述 众所周知,Alice和Bob非常喜欢博弈,而且Alice永远是先手,Bob永远是后手. Alice和Bob面前有3堆石子,Alice和Bob每次轮流拿某堆石子中的若干个石子(不可以是0个),拿到所有石子中最后一个石子的人获胜.这是一个只有3堆石子的Nim游戏. Bob错误的认为,三堆石子的Nim游戏只需要少的两堆的石子数量加起来等于多的那一堆,后手就一定会胜利.所以,Bob把三堆石子的数量分别设为 {k,4k,5k}(k>0). 现在Alice想要知道,在k 小于 2^n 的时候,有多

2011 ACM/ICPC 福州赛区现场赛8.7

A题是哪个象棋的题: B题hdu4122: 好像是水过去的,看到题解要用队列,就是简单的枚举加判断条件过去的. C题hdu4123: 用到MRQ算法查询区间内的最大最小值 G题hdu4127 是个填颜色的题: 是把所有点分成3部分,已经和00连通的,还有就是下一次需要填的颜色部分,剩下的部分就是填不了的, 用IDA算法,分开写几个小程序,填充颜色同一个颜色的程序,当前状况需要改变最少颜色的次数,还有就是染某个颜色加入格子的数(这个就是看染着个颜色有没有用) 再有就是IDA程序,每次都是跑改变成0

zoj 3662 第37届ACM/ICPC长春赛区H题(DP)

题目:给出K个数,使得这K个数的和为N,LCM为M,问有多少种 f[i][j][k]表示选i个数,总和为j,最小公倍数为k memery卡的比较紧,注意不要开太大,按照题目数据开 这种类型的dp也是第一次做 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue&g

hdu 4462 第37届ACM/ICPC 杭州赛区 J题

题意:有一块n*n的田,田上有一些点可以放置稻草人,再给出一些稻草人,每个稻草人有其覆盖的距离ri,距离为曼哈顿距离,求要覆盖到所有的格子最少需要放置几个稻草人 由于稻草人数量很少,所以状态压缩枚举,之后慢慢判断即可,注意放稻草人的格子是不需要覆盖的 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath>

hdu 4461 第37届ACM/ICPC杭州赛区I题

题意:给两个人一些棋子,每个棋子有其对应的power,若b没有或者c没有,或者二者都没有,那么他的total power就会减1,total power最少是1,求最后谁能赢 如果b或c出现的话,flag就标记为1,那么在判断的时候如果flag==0,就说明他们没出现过,那么就要-1,然后就wa了,必须要两个变量判断,不知道为什么 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #inclu

hdu 4463 第37届ACM/ICPC杭州赛区K题 最小生成树

题意:给坐标系上的一些点,其中有两个点已经连了一条边,求最小生成树的值 将已连接的两点权值置为0,这样一定能加入最小生成树里 最后的结果加上这两点的距离即为所求 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 #include<map>

2017 ACM/ICPC 新疆赛区 I 题 A Possible Tree 带权并查集

传送门 题意:给定一棵带权树的形态, 但是并不知道每天条边的具体权重. 然后给m个信息, 信息格式为u v val, 表示在树上u 到 v 的路径上经过的边的权重的异或和为val, 问前面最多有多少个信息是不冲突的. 思路:首先很明显的我们要维护一系列不知道的信息, 看冲不冲突的那就是带权并查集没跑了, 此时r[v] 表示v到这棵树的根节点(虽然题目没给, 但是我们可以假设一个)的路径异或和, 那么此时的每条信息相当于是告诉你r[u] ^ r[v]的值, 注意异或的特性. 所以对于每条信息维护好

UVA 1500 - Alice and Bob(博弈)

UVA 1500 - Alice and Bob 题目链接 题意:alice和bob这对狗男女play a game,黑板上有n个数字,每次能把一个数字减1,或者把两个数字合成一个数字,值为两数的和,数字减到0就自动被擦去,最后不能操作的算输,alice先手,问最后谁赢 思路:博弈问题,首先想到一点就很好办了,就是对于非1的所有数,肯定会优先去合并成一个数字的,因为如果当前状态能胜,我优先合并掉,对手不管做什么都无法阻止.然后利用必胜态必败态去进行dp,dp[i][j]记录是有i个1,在非1的堆

hdu 4438 第37届ACM/ICPC 天津赛区现场赛H题

题意:Alice和Bob两个人去打猎,有两种(只)猎物老虎和狼: 杀死老虎得分x,狼得分y: 如果两个人都选择同样的猎物,则Alice得分的概率是p,则Bob得分的概率是(1-p): 但是Alice事先知道Bob先选老虎的概率是Q,问Alice得分的期望最大值是 求期望 如果先去打老虎,则会有bob先去打狼和bob去打老虎两种情况,期望相加则是alice去打老虎的期望,然后求打狼的期望,比较大小即可 1 #include<cstdio> 2 #include<iostream> 3