这是一道很经典的必胜策略题目
原题: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;
}
来源:CSDN
作者:执笔灬绘繁华
链接:https://blog.csdn.net/nimabide_01/article/details/104022095