BZOJ3895 rock

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3895

看这题感觉好神。

SG函数,dp。。。。好像都不行呀。

最后去膜拜题解发现记忆化搜索 囧

那我就直接上他的做法了。

假设每堆石子的数量都大于1

那么我们定义操作数b为当前石子总数+当前堆数-1

若b为奇数,则先手必胜,否则后手必胜

证明:

  若当前只有一堆,则正确性显然

  否则:

    若b为奇数,那么先手只需进行一次合成操作,此时操作数会-1,且仍不存在大小为1的堆,因此只需要证明b为偶数时先手必败即可

    若先手选择了合成操作,那么操作数-1且不存在大小为1的堆,状态回到了b为奇数的状态

    若先手取走了某个大小>=3的堆中的一个石子,那么操作数-1且不存在大小为1的堆,状态回到了b为奇数的状态

    若先手取走了某个大小为2的堆中的一个石子,那么后手只需要将另一个石子与其它堆合成,b的奇偶性不变且仍不存在大小为1的堆

  故b为偶数时先手必败

现在回到一般情况 可能存在大小为1的堆

我们设有a个大小为1的堆,其余堆的操作数为b

那么当前的状态就可以用一个二元组(a,b)来表示

然后就好说了,直接进行记忆化搜索,分情况讨论。

#include <cstdio>
#include <cstring>
#include <algorithm>

#define N 1010

using namespace std;

int f[60][50500];
int n;

int dfs(int num1,int steps){
    if(!num1) return steps&1;
    if(steps==1) return dfs(num1+1,0);
    if(~f[num1][steps]) return f[num1][steps];
    int &ans=f[num1][steps];
    if(num1){
        if(!dfs(num1-1,steps)) return ans=1;//取走1的一堆
        if(steps && !dfs(num1-1,steps+1)) return ans=1;    //将一个1加入多堆中
    }
    if(num1>1){
        if(steps && !dfs(num1-2,steps+3)) return ans=1; //合并两堆1
        if(!steps && !dfs(num1-2,steps+2)) return ans=1;
    }
    if(steps && !dfs(num1,steps-1)) return ans=1;    //普通的合并与取石子 
    return ans=0;
}

int main(){
    memset(f,-1,sizeof(f));
    scanf("%d",&n);
    while(scanf("%d",&n)==1){
        int a=0,b=-1; 
        for(int i=1,x;i<=n;i++){
            scanf("%d",&x);
            if(x==1) a++;
            else b+=x+1;
        }
        puts(dfs(a,b)? "YES":"NO");
    }
    return 0;
}
View Code

 

posted @ 2015-06-03 20:27  lawyer'  阅读(182)  评论(0编辑  收藏  举报