URAL1087 The Time to Take Stones
取石子问题升级版
Description
You probably know the game where two players in turns take 1 to 3 stones from a pile. Looses the one who takes the last stone. We’ll generalize this well known game. Assume that both of the players can take not 1, 2 or 3 stones, but k1, k2, …, km ones. Again we’ll be interested in one question: who wins in the perfect game. It is guaranteed that it is possible to make next move irrespective to already made moves.
Input
The first line contains two integers: n and m ( 1 ≤ n ≤ 10000; 1 ≤ m ≤ 50 ) — they are an initial amount of stones in the pile and an amount of numbers k1, …, km. The second line consists of the numbers k1, …, km, separated with a space ( 1 ≤ ki ≤ n).
Output
Output 1, if the first player (the first to take stones) wins in a perfect game. Otherwise, output 2.
Sample Input
input
17 3
1 3 4
output
2
题目大意:有两个人取石子,先手是编号为1的人,取到最后那个石子的人就输了。输入石子个数n,有m种取法。再输入每种取法分别能取多少个石子。输出赢的那个人的编号(1或者2)。
其实,做法是类似于DP的(其实就是)。
首先考虑一个问题,如果这堆石子只有一个。那么先手会赢吗?(显然是输家)。
再考虑一个问题:如果现在已经知道在剩余a个石子时取,先手一定会输,那么假设b是那m种选法中任意一种,对于a+b个石子,先手的胜负情况可以确定吗?
答案是显然会赢。
为了方便表示,用1和0表示先手的胜和负。
再考虑一个命题:如果现在已经知道在剩余a个石子时取,先手的胜负情况是f,那么假设b是那m种选法中任意一种,对于a+b个石子,先手的胜负情况一定是f^1。
这是显然的。
那么,我们的dp状态转移方程已经差不多了。
直接看代码吧:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
int f[10010],a[100];
int main(){
int i,j,k,m,n;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)scanf("%d",a+i);
f[1]=0;//如果只剩1个石子,显然先手会输
for(i=2;i<=n;i++){//计算剩余石子为2~n的情况
f[i]=0;//假设全都会输
for(j=1;j<=m;j++)
if( (i-a[j]>0)&&(f[i-a[j]]==0) ){//仔细琢磨就会发现其实这就是上面那个命题
f[i]=1;//如果会赢,就不用再算了
break;
}
}
printf("%d\n",f[n]==1?1:2);//根据先手(1)的胜负情况输出
return 0;
}
是不是很简单的啊~