小乐乐吃糖豆
小乐乐是一个比较喜欢吃糖豆的小孩子,小乐乐的哥哥大乐乐也同样爱吃糖豆。
作为一个小孩子,他们永远觉得谁吃掉了最后一个糖豆,谁吃的糖豆最多。
为了公平起见小乐乐与大乐乐商量吃糖豆的规则如下:
小乐乐与大乐乐轮流吃糖豆。
小乐乐作为弟弟,小乐乐先吃糖豆。
小乐乐第一次可以吃任意不超过n 个糖豆。(n为糖豆初始总数)
后一个人每次能吃【1,前一个人吃的糖豆*2】
> 已知有n个糖豆,小乐乐与大乐乐开始分食糖豆,小乐乐与大乐乐都采用最优策略,请问谁能吃到最后一粒糖豆?
分析:
明显的博弈题,属于组合游戏的范畴,对于每个状态dp[i][j] 代表可以取1~i个糖果,还剩j个糖果的状态,
如果这个状态的后继状态存在必输状态,那么这个状态则是必胜状态,否则是必输状态,
剪枝操作
j=0,是必输状态
i>=j 是必胜状态
if j是3的倍数取 1~(j%3-1)的糖果,否则 取1~(j%3)的糖果。
因为处于你这个状态想要必胜,你就要尽力寻找后继为必输状态,其他取法部分可能存在后继为必输状态的。
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
#include<iomanip>
#include<map>
#include<vector>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxn=1e3;
const int branch=26;
const int inf=0x3f3f3f3f;
const int MOD=1e6+7;
int dp[maxn+10][maxn+10];
int book[maxn+10][maxn+10];
int dfs(int i,int j);
void init()
{
mset(dp,0);
mset(book,0);
for(int i=0; i<=maxn; ++i)
book[i][0]=1;//i 0 状态已经计算过了 是必输状态
for(int i=1; i<=maxn; ++i)
for(int j=1; j<=i; ++j)
{
dp[i][j]=1;//必胜状态
book[i][j]=1;
}
}
int dfs(int i,int j)//计算i j状态是必胜还是必输状态 ,此人面对的状态是 可以取i个, 还剩j个的状态
{
/*
1.寻找该状态下 有没有必输状态
*/
if(book[i][j])
return dp[i][j];
/* 此时 i>=1 j>=1 i<j*/
for(int p=1; p<=i; ++p) //取q 个
{
if(!dfs(2*p,j-p))
{
book[i][j]=1;
return dp[i][j]=1;
}
}
book[i][j]=1;
return dp[i][j]=0;
}
int main()
{
int n;
init();
while(~scanf("%d",&n))
{
if(dfs(max(1,n-1),n))
printf("Small
");
else
printf("Big
");
}
// freopen("../text.txt","w",stdout);
// for(int i=1; i<=1000; ++i)
// {
// if(!dfs(max(1,i-1),i))
// printf("%d
",i);
// }
// return 0;
}
另外遍历之后发现先手输的状态只有(后者胜,输出"Big")
2
3
5
8
13
21
34
55
89
144
233
377