HDU 4111 Alice and Bob 【DP解决博弈】

http://acm.hdu.edu.cn/showproblem.php?pid=4111

题目大意:详见http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=1719
解题思路:开始的时候一直在推规律。但是很苦x,比赛到最后都没推出来。
看了解题报告的状态转移方程才ac了此题。
最终的石子都会化成一堆。但是如果合并的话将奇偶性改变了一下,相当于缓了一步。
由于一堆石子只有一个很特殊。因此状态转移方程将他分开考虑。
dp[i][j]:表示i个1堆石子 ,将非一石子合并后的总数。dp[i][j]=1,表示Alice赢,否则输。因此有以下种状态转移而来:
拿走一个1的堆:dp[i-1][j]
合并2个1的堆:dp[i-2][j+3](当i>=2的时候) dp[i-2][2](j==0)
大堆拿一个:dp[i][j-1]
1的堆与大堆合并:dp[i-1][j+1];

代码如下:

View Code
#include<stdio.h>
#include<string.h>
int dp[55][50005];
int main()
{
    int n, i, j, num1, sum, a, T, cas=0;
    memset(dp, 0, sizeof(dp)); 
    for(i=0; i<=50001; i++)
        dp[0][i]=i%2;
    for(i=1; i<=52; i++)
        i%3==0?dp[i][0]=0:dp[i][0]=1;
    for(i=1; i<=52; i++)
        for(j=0; j<=50001; j++)
        {
            if(j==1)   //大堆出现1 
            {
                dp[i][j]=dp[i+1][0]; continue;
            }
            if(dp[i-1][j]==0||(dp[i-1][j+1]==0&&j!=0)||(j!=0&&dp[i][j-1]==0)) //拿走小堆的一个 or 一个小堆与大堆合并 or 拿走大堆的一个 
            {
                dp[i][j]=1; continue;
            }
            if(i>=2) 
            {
                if((j==0&&dp[i-2][2]==0)||(j>=1&&dp[i-2][j+3]==0)) //2个小堆对的合并 
                {
                    dp[i][j]=1; continue;
                }
            }
        }
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        num1=0, sum=0;
        for(i=0; i<n; i++)
        {
            scanf("%d", &a);
            if(a==1) num1++;
            else sum+=(a+1);
        }
        if(sum) sum--;
        cas++;
        printf("Case #%d: ", cas);
        if(dp[num1][sum]) printf("Alice\n");
        else printf("Bob\n");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Hilda/p/2998662.html