zoukankan      html  css  js  c++  java
  • 【题解】一本通例题 取石子游戏

    Link

    ( ext{Solution:})

    由于有两种操作,不太好搞。

    观察到,如果不考虑合并,显然总石子数为奇数则先手必胜,否则必败。

    对于一个局面,显然我们最多操作次数是((sum a_i)+n-1)

    定义(b=n-1+sum a_i),则如果不存在石子数为(1)的堆,同样有(b)为奇数必胜。

    证明:若堆数为(1)显然。

    (b)是奇数,则先手选择合并两堆使得(b)为偶数。那么下面证明(b)为偶数必败。

    若此时合并两堆,则下一次对手也合并两堆,没有用。

    如果这时取走一个石子数(leq 3)的堆的石子,则我继续合并,(b)奇偶性不变。

    若这时取走一个石子数(=2)的堆的石子,则我把剩下的一个并到其它石子中,(b)奇偶性不变。

    证毕。

    因为(n<=50,)我们可以考虑记忆化搜索。设(dp[i][j])表示石子数为(1)的堆数为(i),剩下的操作数为(j)是否有必胜策略。

    那么我们可以从下面四个方面转移:

    • 拿走一个石子数为(1)的堆:(dp[i-1][j])

    • 合并两个石子数为(1)的堆:(dp[i-2][j+2+(j>0)])

    • 合并一个石子与另一个多的石子:(dp[i-1][j+1])

    • 拿掉一个石子:(dp[i][j-1])

    #include<bits/stdc++.h>
    using namespace std;
    int T,n;
    char dp[51][60000];
    char dfs(int a,int b){
    	if(a==0)return dp[a][b]=(b&1);
    	if(b==1)return dp[a][b]=dfs(a+1,0);
    	if(~dp[a][b])return dp[a][b];
    	char &F=dp[a][b];
    	if(a>=2&&!dfs(a-2,b+2+(b?1:0)))return F=true;
    	if(b&&!dfs(a,b-1))return F=true;
    	if(a&&b&&!dfs(a-1,b+1))return F=true;
    	if(a&&!dfs(a-1,b))return F=true;
    	return F=false;
    }
    int main(){
    	scanf("%d",&T);
    	memset(dp,-1,sizeof(dp));
    	while(T--){
    		scanf("%d",&n);int b=-1,a=0;
    		for(int i=1;i<=n;++i){
    			int x;scanf("%d",&x);
    			if(x==1)a++;
    			else b+=x+1;
    		}
    		if(b==-1)b=0;
    		puts(dfs(a,b)?"YES":"NO");
    	}
    	return 0;
    }
    
  • 相关阅读:
    u-boot 2011.09 调用kernel 的流程
    Delphi repeat Until 运用
    clientdataset的使用
    类型TTreeView.items.add 与 TTreeView.items.addchild有何区别?(10分)
    delphi中nil、null、UnAssigned区别
    操作TreeView(咏南工作室)
    delphi7 treeview + 数据库 实现动态节点维护
    Delphi Try Except 实例
    Delphi 中自定义异常及异常处理的一般方法
    Delphi中的异常处理(10种异常来源、处理、精确处理)
  • 原文地址:https://www.cnblogs.com/h-lka/p/13088261.html
Copyright © 2011-2022 走看看