求sg值——分析与找规律

题目: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 
#include 
#include 
#include 
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
#include
#include
#include
#include
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 
#include 
#include 
#include 
#include 
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;
}
附:

函数名: ceil(向上取整)
用 法: double ceil(double x);
功 能: 返回大于或者等于指定表达式的最小整数
头文件:math.h
返回数据类型:double
另外,向下取整函数为floor.

                    

你可能感兴趣的:(博弈)