UVA-10404 Bachet's Game

我的未来我决定 提交于 2020-01-25 14:45:12

这是一道很经典的必胜策略题目

原题:Stan和Ollie在玩一个游戏,桌子上有n(n <= 1e6)枚硬币,同时给出k(k <= 10)个互不相同的正整数数a[1], a[2],  a[3], ..., a[k],Stan跟Ollie轮流拿桌子上的硬币,但每次只能拿这k个数中的一个数的枚数(即设这k个数构成一个集合,Stan跟Ollie只能从这集合中挑出一个数来作为他本回合拿走硬币的数量),每一局游戏都是Stan先拿,这k个数中必有1,所以每一局游戏必然能分出胜负

现在定你n, k, 以及后边的k个数,假设他们两个每一步都做出了最佳策略,那么请问最终会是谁获胜?

输入:

有多行,每行第一个数是n,第二个数是k,然后后边接着k个数

输出:

对于每行都要输出一个结果,即输出"Stan wins"或者"Ollie wins"

样例输入:

20 3 1 3 8

21 3 1 3 8

22 3 1 3 8

23 3 1 3 8

1000000 10 1 23 38 11 7 5 4 8 3 13

999996 10 1 23 38 11 7 5 4 8 3 13

样例输出:

Stan wins

Stan wins

Ollie wins

Stan wins

Stan wins

Ollie wins

 

原题链接(英文):https://vjudge.net/problem/UVA-10404


这道题,实际上是一道DP题,状态status[j]表示轮到Stan回合时,桌子上剩下j枚硬币,此时Stan是处于必胜态还是处于必败态

那么很显然status[0]的状态应该是必败态。

可是状态如何转移呢?

我们现在看向status[j]这个状态,我们看看这个状态下Stan必胜或者必败是怎么决定下来的(status[j]的值是怎么确定的):

现在桌子上有j枚硬币,Stan盯着这些硬币,思考着怎样拿他才能获胜

思考一番后,他突然明白,只要他拿走硬币后,剩下的硬币数量是对手的必败态,那么他就能获胜,用数学的形式去表达的话就是,设1 <= i <= k,如果存在一个a[i],使得status[j - a[i]] == 必败态,那么status[j]就一定是必胜态

相反,如果不存在status[j - a[i]] == 必败态,也就是说,不管Stan怎么拿硬币,轮到Ollie的回合时Ollie都能处在必胜态的话,那么Stan此时是处于必败态,也就是status[j] = 必败态。


这里可能有点难理解,为什么这个status[j - a[i]]的必败态能表示Ollie的必败态,当初status不是用来表示Stan的状态的么?虽然从最终结果上看整个status是在表示Stan的状态的,但在实际的计算过程中,当桌子上剩余x枚硬币时,不论是轮到谁,status[x]都表示他当前所处的状态是必胜还是必败,也就是说,之所以我们最终结果看整个status是在表示Stan的状态,是因为我们默认上是对于整个status的状态下都是Stan先手,所以这个数组才都表示Stan的状态的。这样一来,status[j - a[i]]能表示Ollie的状态也能解释得清了,因为当前轮到Stan的回合下,桌子上剩下j枚硬币,当Stan拿走了a[i]枚硬币后,此时桌子上剩下j - a[i]枚硬币,是Ollie的回合,所以这时候的status[j - a[i]]所表示的,就是Ollie的状态。


知道必胜必败态之间怎么转移后,代码也就好写了

for (int j = 1; j <= n; j++)
	for (int i = 0; i < k; i++)
		if (j - a[i] >= 0 && status[j - a[i]] == Lose) status[j] = Wins;

整个代码:

#include<iostream>
#include<cstring>
#define Wins true
#define Lose false
using namespace std;

const int Maxn = 1e6 + 10;
bool status[Maxn];
int a[15];

int main()
{
	int n;
	while (cin >> n) {
		memset(status, 0, sizeof(status));
		int k;
		cin >> k;
		for (int i = 0; i < k; i++)
			cin >> a[i];

		for (int j = 1; j <= n; j++)
			for (int i = 0; i < k; i++)
				if (j - a[i] >= 0 && status[j - a[i]] == Lose) status[j] = Wins;

		if (status[n] == Wins) cout << "Stan wins" << endl;
		else cout << "Ollie wins" << endl;
	}

	return 0;
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!