题目:hdu1517
题意:你和一个人玩游戏,给你一个数字n,每次操作可以从2~9中任选一个数字,并把它与p相乘,(游戏开始时p=1)
两人轮流操作,当一个人操作完后p>=n,这个人就是胜者。
解答:方法一:写个求sg值的函数,然后找规律。
方法二:首先,有以下结论:1.任意操作都可将奇异局势变为非奇异局势。(必败状态转换为必胜状态)
2.采用适当的方法,可以将非奇异局势变为奇异局势。(必胜状态转换为必败状态)
(即转化为必败态需要一些条件)
我们解决这个问题,就是要求sg[1]的值是否为0.首先,当一个状态x>=n的时候肯定是必败的。而当这个必败状态除以9(并且向上取整),它就可以转换为必胜状态。再除以2(以后均为向上取整),又转换为必败状态,因为如果某一范围的数通过乘2到9可以转换为必败你依然有机会赢。所以它除以2(向上取整),它就没办法保留必败状态,所以你仍然会输。所以就如此往复。除以9变为必胜,除以2变为必败。
方法一的代码(自己找规律发现的)
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int n;
int main()
{
while(cin >> n){
if(n >= 10 && n <= 18)
cout << "Ollie wins." << endl;
else if(n >= 163 && n <= 324)
cout << "Ollie wins." << endl;
else if(n >= 2917 && n <= 5832)
cout << "Ollie wins." << endl;
else if(n >= 52489 && n <= 104976)
cout << "Ollie wins." << endl;
else if(n >= 944785 && n <= 1889568)
cout << "Ollie wins." << endl;
else if(n >= 17006113 && n <= 34012224)
cout << "Ollie wins." << endl;
else if(n >= 306110017 && n <= 612220032)
cout << "Ollie wins." << endl;
else
cout << "Stan wins." << endl;}
return 0;
}
附:打表的时候求sg函数的代码:
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
using namespace std;
const int MAXN = 10000;
int n;
int sg[MAXN];
int getsg(int v)
{
if(sg[v]!=-1)
return sg[v];
int vis[10000];
memset(vis,0,sizeof(vis));
int i;
for(i = 2;i <= 9;i++)
{
int tmp = i * v;
if(tmp >= n)
{
vis[0] = 1;
}
else
{
sg[tmp] = getsg(tmp);
vis[sg[tmp]] = 1;
}
}
for(int i = 0;;i++)
{
if(!vis[i])
{
sg[v] = i;
return i;
}
}
}
int main()
{
for(int j = 1;j <= 8000;j++){
n = j;
memset(sg,-1,sizeof(sg));
int t = getsg(1);
cout << n << ": " << t << endl;
}
return 0;
}
方法二:(借鉴了网上的思路)
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
int n;
int main()
{
int n,x;
while(~scanf("%d",&n))
{
for(x = 0;n > 1;x++)
{
if(x&1)
n = ceil(n * 1.0 / 2)
else
n = ceil(n * 1.0 / 9);
}
puts(x&1?"Stan wins.":"Ollie wins.");
}
return 0;
}
附: