题意:n堆不同数量的石头 A,B两人可进行如下两种操作
1. 拿走其中一堆的其中一个石头
2.合并任意两堆石头
A先,直达轮到某人事,他无法进行任何操作,结束,并判该人输。
题解:
无论石头怎么堆, 操作次数的奇偶性确定,即可知道比赛结果。
但是, 如果某堆石头只要一个,那么拿走他的同时, 合并的操作数也会减少一次,结果反转。
但是可以推出,如果只有一个堆石子数是1, 那么该状态是必胜状态, 如果 1+奇数, 合并, 为偶, 1+偶数, 拿走哪个一, 为偶。
由于两人是足够聪明的(都用最佳策略),不会让原本不是数量为一的堆被自己拿到只有一然后然对数改变自己的必胜状态。为一时就合并。
所以, 只要我手上的状态可以转变成一个必败状态丢给对手,自己就必胜。
我们以后推出 堆为一的数量为0,和1,的状态,更具这两种状态进行状态转移即可。
dp[ i ][ j ] // i 为一个堆数, j 其余的操作数
一个有四种状态转移
1. 消去一个 i堆, dp(i-1,j)
2. 其中一个i堆合并倒 j 堆 dp(i-1,j-1)
3,其中两个 i 堆合并 dp(i-2,j+3)
4. 消去j堆的一个操作 dp(i,j-1)
只要以上四种转移状态有一个必败, 当前状态必胜。
处理好边界问题,这题就能敲啦
//code
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int dp[55][55000];
int DP(int i, int j)
{
if(dp[i][j]!=-1) return dp[i][j];
if(j==1) return DP(i+1,0);
dp[i][j]=0;
if(i==0)
{
if(j%2) dp[i][j]=1;
}
if (i>=1)
{
if (!DP(i-1,j))return dp[i][j]=1;
if (j&&!DP(i-1,j+1))return dp[i][j]=1;
if (i>=2)
{
if ((!j)&&!DP(i-2,2))return dp[i][j]=1;
if ((j)&&!DP(i-2,j+3))return dp[i][j]=1;
}
}
if ((j>=2)&&!DP(i,j-1))return dp[i][j]=1;
return dp[i][j];
}
int main()
{
memset(dp,-1,sizeof(dp));
int T;
cin>>T;
int cnt=1;
while(T--)
{
int n;
cin>>n;
int i,j;
i=j=0;
for(int k=0; k<n; k++)
{
int tmp;
cin>>tmp;
if(tmp == 1) i++;
else j+=tmp+1;
}
j--;
DP(i,j);
if(dp[i][j]) printf("Case #%d: Alice\n",cnt++);
else printf("Case #%d: Bob\n",cnt++);
}
return 0;
}