原题:
Bachet’s game is probably known to all but probably not by this name. Initially there are n stones on the table. There are two players Stan and Ollie, who move alternately.Stan always starts. The legal moves consist in removing at least one but not more than k stones from the table. The winner is the one to take the last stone.Here we consider a variation of this game. The number of stones that can be removed in a single move must be a member of a certain set of m numbers. Among the m numbers there is always 1 and thus the game never stalls.
Input
The input consists of a number of lines. Each line describes one game by a sequence of positive numbers.
The first number is n ≤ 1000000 the number of stones on the table; the second number is m ≤ 10
giving the number of numbers that follow; the last m numbers on the line specify how many stones can
be removed from the table in a single move.
Output
For each line of input, output one line saying either ‘Stan wins’ or ‘Ollie wins’ assuming that both
of them play perfectly.
Sample Input
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
Sample Output
Stan wins
Stan wins
Ollie wins
Stan wins
Stan wins
Ollie wins
大意:
两个人玩取石子的游戏,现在给出一堆石子有n个,然后给你m个数表示每次可以拿走的石子数,拿走最后一枚石子的人获胜,每次stan先拿。问大家都用最优的拿法,谁是最后的赢家?
#include<iostream>
#include<algorithm>
#include<map>
#include<string>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<vector>
#include<cmath>
#include<stack>
#include<queue>
#include<iomanip>
#include<set>
#include<fstream>
#include <climits>
using namespace std;
//fstream input,output;
bool dp[1000001];
int stone[11];
int n,tot;
int main()
{
ios::sync_with_stdio(false);
while(cin>>tot)
{
memset(dp,false,sizeof(dp));
dp[0]=false;
cin>>n;
for(int i=1;i<=n;i++)
cin>>stone[i];
sort(stone+1,stone+1+n);
for(int j=1;j<=tot;j++)
{
for(int i=1;i<=n&&j>=stone[i];i++)
{
if(!dp[j-stone[i]])
{
dp[j]=true;
break;
}
}
}
if(dp[tot])
cout<<"Stan wins"<<endl;
else
cout<<"Ollie wins"<<endl;
}
解答:
刚看到这题寻思用sg函数解。不过看到标签里面写的是dp,一下就意识到是用完全背包的方法来解答。
设置dp[i]表示有i个石子的时候先拿石子的人是输是赢,如果是赢就标记为true,反之为false。
现在考虑拿石子游戏的规则,如果在当前状态i,也就是又i个石子的时候,如果你拿走一些石子后剩下的石子数中存在一个必败状态,也就是dp[i-stone[j]]中存在一个false,那当前dp[i]就应该是true。相反,如果dp[i-stone[j]]全是true,也就是无论你怎么石子,拿完以后的状态都是先手必胜,那必然是对手赢了,此时dp[i]=false。
这里说明一下遍历方式和完全背包的代码并不一样,应该先判断当前的石子数j里面拿走石子stone[i]的所有状态。所以,里层循环应该是stone。