zoukankan      html  css  js  c++  java
  • 【BZOJ】3895: 取石子

    【算法】博弈论+记忆化搜索

    【题意】给定n堆石子,两人轮流操作,每个人可以合并两堆石子或拿走一个石子,不能操作者输,问是否先手必胜

    【题解】

    首先,若所有石子堆的石子数>1,显然总操作数为(石子数+石子堆数-1),奇数先手必胜,偶数先手必败。

    若有部分石子堆的石子数=1,情况较复杂,考虑一下五种情形:

    1. 拿走石子数=1的石子堆

    2.减少操作次数(拿走石子或合并石子堆)

    3.操作数减至1时,视为多一堆石子数=1的石子堆(若操作数不为1,即使出现也会被再次操作抵消)

    4.合并两个石子数=1的石子堆

    5.合并一个石子数=1和一个石子数>1的石子堆

    对于(石子数=1的石子堆数(<=50),总操作数(<=50049))二元组进行记忆化搜索。(记忆化是针对所有数据的统一记忆化,这样50*50049就不会超时)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    int n,a[100],f[100][50100];
    int dfs(int x,int y)
    {
        if(~f[x][y])return f[x][y];
        if(x==0)return y&1;
        if(y==1)return dfs(x+1,0);//
        if(x&&!dfs(x-1,y))return f[x][y]=1;
        if(y&&!dfs(x,y-1))return f[x][y]=1;
        if(x>1&&!dfs(x-2,y+2+(y?1:0)))return f[x][y]=1;
        if(x&&y&&!dfs(x-1,y+1))return f[x][y]=1;
        return f[x][y]=0;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        memset(f,-1,sizeof(f));
        while(T--)
        {
            scanf("%d",&n);
            //memset(f,-1,sizeof(f));
            int tmp=0,sum=0;
            for(int i=1;i<=n;i++){scanf("%d",&a[i]);if(a[i]==1)tmp++;else sum+=a[i];}
            if(dfs(tmp,n-tmp-1+sum))printf("YES
    ");else printf("NO
    ");
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Linux内核分析作业六
    课本第三章读书笔记
    课本第十八章读书笔记
    Linux内核分析作业五
    课本第五章读书笔记
    MSF MS11-050/10-087/Adobe攻击实践及内存保护技术
    Linux课题实践五——字符集总结与分析
    Linux课题实践四——ELF文件格式分析
    Linux课题实践三——程序破解
    实践二——内核模块
  • 原文地址:https://www.cnblogs.com/onioncyc/p/7248823.html
Copyright © 2011-2022 走看看