题目链接
3 3 1 1 2 2 3 4 3 2 3 5
Case #1: Alice Case #2: Bob Case #3: Bob
题意:两个人博弈,N堆石子,每次可以拿掉一个石子,也可以把两堆合并成一堆,不能操作的那个人输,问先手赢还是输。
题解:假设每堆石子的个数都大于一。如果一个 人面对状态的最大操作数为奇数,那么他可以贪心的先把石子合并成一堆,这样的操作数就是最大操作数并且为奇数,这个人一定赢。如果一个人面对状态的最大操作数位偶数,由于没有1,另一个一定可以贪心的让最大操作数不变,那么这个人一定输。那么能够影响操作数的奇偶性的只有1。所以我们可以先把非1的堆合成一堆。那么现在的状态就是1的个数和非1的堆的大小,非1的堆的大小不超过6*10^4,1的个数不超过10^3,。总状态数位10^7的数量级,我们可以用记忆化搜索的方法,求出每个状态是必胜点还是必败点。其实有效状态数远小于10^7,复杂度就是状态数,<O(10^7)。
我们用dp[ i ][ j ]表示非1堆的大小为 i ,1的个数为 j 的状态是必胜点还是必败点。石子怎么操作就怎么转移就是了。转移的时候要注意,非1堆的大小不能为1。
详情见代码:
//#pragma comment(linker, "/STACK:102400000,102400000") #include<stdio.h> #include<queue> #include<iostream> #include<algorithm> #include<string.h> #include<string> #include<math.h> #include<stack> #define nn 55 #define mod 1000 #define inff 0x3fffffff typedef __int64 LL; using namespace std; int n; int dp[60000][55]; int a[nn]; int dfs(int sum,int yi) { if(dp[sum][yi]!=-1) return dp[sum][yi]; dp[sum][yi]=0; if(yi==0) { return dp[sum][yi]=sum%2; } if(sum==1)//非1堆的大小不能为1 { dp[sum][yi]=dfs(0,yi+1); return dp[sum][yi]; } dp[sum][yi]|=(1-dfs(sum,yi-1));//拿掉一个1 if(sum) { dp[sum][yi]|=(1-dfs(sum-1,yi));//从非1堆中拿掉一个石子 dp[sum][yi]|=(1-dfs(sum+1,yi-1));//将一个1合并到非1堆 } if(yi>=2)//合并两个1 { if(sum) dp[sum][yi]|=(1-dfs(sum+3,yi-2)); else dp[sum][yi]|=(1-dfs(sum+2,yi-2)); } return dp[sum][yi]; } int main() { int i,t; int cas=1; memset(dp,-1,sizeof(dp)); scanf("%d",&t); while(t--) { scanf("%d",&n); int sum=0,yi=0; for(i=1;i<=n;i++) { scanf("%d",&a[i]); if(a[i]==1) yi++; else sum+=a[i]+1; } if(sum) sum--; printf("Case #%d: ",cas++); if(dfs(sum,yi)) { puts("Alice"); } else puts("Bob"); } return 0; }