1831 小C的游戏 (规律or记忆化搜索)

来源:互联网 发布:珠海知想科技官网 编辑:程序博客网 时间:2024/06/05 06:22


1831 小C的游戏
 收藏
 关注
小C和小L是好朋友,她们在玩一个游戏。
一开始有一个大小为n的石子堆,小C先手。
每次可以对这个石子堆拿走一个或者把这个石子堆分成等量的几份并只取其中一份(不能不变或只剩下一个)。
如果取走最后一个人的算败,请问这个游戏小C是否能胜。
Input
一行表示数据组数Tcases(Tcases<=1,000)。后面Tcases行每行一个n(n<=1,000,000,000)。
Output
有Tcases行对于先手获胜输出“TAK”,先手狗带输出“NIE”。
Input示例
15
Output示例
NIE

贴一份官方题解:

最简单的做法就是找规律了,直接搜一下就能获得所有的胜负态。 
仔细观察可以发现质数除了2和17就是败的,合数除了16,34和289都是赢的。 
感觉这样是不太科学的,那就来讲讲道理。 
我们发现2,4,8都是赢的,而16的后继状态都是赢的,所以它是败的,而2^n(n>4)都能转化到16。 
同样的我们能说明17和2^n17^m。 
我们考虑一个合数,它的因数肯定有个败态的,它就必胜了。 
这样也就说明了质数是必败了。

如果打表的话, 茫茫一堆数里找到规律有点难,首先猜结论,可能与质数合数有关,然后打表的时候把质数合数晒出来。。

打表规律代码(附打表代码)

#include <iostream>#include <cstring>#include <algorithm>#include <cstdio>using namespace std;const int maxn = 4e4 + 5;const int maxx = 3e2 + 5 ;int dp[maxn], prime[maxn];void get_data(){    dp[1] = 0; dp[2] = 1; dp[3] = 0; dp[4] = 1;    dp[5] = 0;    for(int i = 5; i <= maxx; i++)    {        dp[i] = dp[i-1] ? 0 : 1;        for(int j = 2; j < i; j++)        {            if(i%j == 0)            {                if(!dp[j])                {                    dp[i] = 1;                    break;                }            }        }    }}void get_prime(){    for(int i = 2; i <= maxn; i++)    {        if(!prime[i])        {            for(int j = i*2; j <= maxn; j += i)                prime[j] = 1;        }    }}int main(){    int t, n;//    get_data();//    get_prime();//    for(int i = 1; i <= maxx; i++)//    {//        if(dp[i] && !prime[i])//        {//            cout << "质数 Win" << ' ';//            cout << i << endl;//        }//        else if(!dp[i] && prime[i])//        {//            cout << "合数 Lose" << ' ';//            cout << i << endl;//        }////        printf("%5d%s", i, dp[i] ? "WIN   " : "LOSE   ");////        if(i % 5 == 0) cout << endl;//    }    cin >> t;    while(t--)    {        scanf("%d", &n);        int temp = n;        for(int i = 2; i*i <= temp; i++)        {            while(temp%i == 0)                temp /= i;        }        if(temp == n)        {            if(n == 2 || n == 17)                cout << "TAK" << endl;            else                cout << "NIE" << endl;        }        else        {            if(n == 16 || n == 34 || n == 289)                cout << "NIE" << endl;            else                cout << "TAK" << endl;        }    }    return 0;}

这题虽然是1e9但是我们记忆化到1e7完全可以,大于1e7的让他自己搜就行,而且这个最大复杂度是1e9-1e7,我们只要把搜因子的代码放在-1代码前面,否则会一直-1,-1,-1,就爆栈了。。。这样更快搜出来算一个小优化。。。以后搜索要注意会不会爆栈。。

#include <iostream>#include <cstring>#include <algorithm>#include <cstdio>using namespace std;const int maxn = 1e7 + 5;int dp[maxn];int dfs(int x){     if(x < maxn && dp[x] != -1) return dp[x];     for(int i = 2; i*i <= x; i++)     {         if((x%i == 0) && (!dfs(i) || !dfs(x/i)))         {             if(x < maxn) return dp[x] = 1;             else return 1;         }     }     if(!dfs(x-1))  //这里一定要放在后面     {         if(x < maxn)            return dp[x] = 1;         else            return 1;     }     if(x < maxn)        return dp[x] = 0;     else        return 0;}int main(){    int t, n;    memset(dp, -1, sizeof(dp));    dp[0] = 0; dp[1] = 0; dp[2] = 1; dp[3] = 0;    cin >> t;    while(t--)    {        scanf("%d", &n);        if(dfs(n))            cout << "TAK" << endl;        else            cout << "NIE" << endl;    }    return 0;}


1 0
原创粉丝点击