码迷,mamicode.com
首页 > 其他好文 > 详细

SP9934 ALICE - Alice and Bob

时间:2021-04-09 13:17:01      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:合并   ase   and   stream   return   using   min   不为   总数   

\(s\) 为石子的总数,那么操作次数最多为 \(s+(n-1)\)

如果石子数量全不为一,那么先手必胜的条件为 \(s+(n-1)\) 为奇数,因为他一定可以保证操作 \(s+(n-1)\) 次。

反之后手必胜。

问题在于石子数量可能为 \(1\) ,这时去掉这颗石子便无法合并。

所以状态应该与石子数量为 \(1\) 的堆数,石子数量不为 \(1\) 的堆的最大操作次数有关。

\(SG(a,b)\) 表示剩下 \(a\) 堆数量为 \(1\) 的石子堆,其余石子堆能操作 \(b\) 次的 \(SG\) 函数值。

那么后继状态有五种种:

  • 在石子数不为 \(1\) 的石子堆取掉一颗石子
  • 合并两堆不为 \(1\) 的石子堆
  • 取掉一堆为 \(1\) 的石子堆
  • 将两堆为 \(1\) 的石子堆合并
  • 将一堆为 \(1\) 和一堆不为 \(1\) 的石子堆合并

边界条件就是最开始讲的。

#include <cstdio>
#include <iostream>
using namespace std;

const int MAXN = 50 , MAXV = MAXN * 1000;
int T , n;

int SG[ MAXN + 5 ][ MAXV + 5 ];
int dfs( int Cnt1 , int oper ) {
	if( SG[ Cnt1 ][ oper ] ) return SG[ Cnt1 ][ oper ];
	if( oper == 1 ) return SG[ Cnt1 ][ oper ] = dfs( Cnt1 + 1 , 0 );
	if( Cnt1 == 0 ) return SG[ Cnt1 ][ oper ] = oper % 2;
	
	int Mex = 2;
	if( Cnt1 ) Mex = min( Mex , dfs( Cnt1 - 1 , oper ) );
	if( oper ) Mex = min( Mex , dfs( Cnt1 , oper - 1 ) );
	if( Cnt1 >= 2 ) Mex = min( Mex , dfs( Cnt1 - 2 , oper + 2 + ( oper ? 1 : 0 ) ) );
	if( Cnt1 && oper ) Mex = min( Mex , dfs( Cnt1 - 1 , oper + 1 ) );
	return SG[ Cnt1 ][ oper ] = Mex == 0 ? 1 : 0;
}

int main( ) {
	scanf("%d",&T);
	for( int t = 1 ; t <= T ; t ++ ) {
		printf("Case #%d: ", t );
		
		int Cnt1 = 0 , oper = 0;
		scanf("%d",&n);
		for( int i = 1 , x ; i <= n ; i ++ ) {
			scanf("%d",&x);
			if( x == 1 ) Cnt1 ++;
			else oper += x + 1;
		}
		if( oper ) oper --; //合并次数=堆数-1
		puts( dfs( Cnt1 , oper ) ? "Alice" : "Bob" );
	}
	return 0;
} 

SP9934 ALICE - Alice and Bob

标签:合并   ase   and   stream   return   using   min   不为   总数   

原文地址:https://www.cnblogs.com/chihik/p/SP9934.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!