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

21 篇文章 1 订阅
20 篇文章 1 订阅


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值