POJ 3688 博弈 + DP

159 篇文章 1 订阅
题意

传送门 POJ 3688

题解

博弈问题,考虑胜负态转移;一般的必胜态只要判断是否能转移到必败态即可,但这个游戏必须要保证必胜态可转移到的非重赛的状态都是必败态。在这个游戏中,存在 4 4 4 种状态:必胜态、必败态、胜负都可能的状态、需要重赛的状态。需要求解的是必胜态的个数。

可以发现先手或后手获胜的情况只与牌上数字的组合有关,与出牌顺序无关。考虑到先手石子堆没有石子时为必败态,那么有:先手必胜时,石子堆的个数只能由奇数个牌上的数字组成。

d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 代表用序号为 [ 0 , i ] [0,i] [0,i] 的牌能构成数字 k k k 时,牌数能否是奇数( j = 1 j=1 j=1)或偶数( j = 0 j=0 j=0),设第 i i i 张牌上的数字为 A [ i ] A[i] A[i]
d p [ i ] [ j ] [ k ] = d p [ i − 1 ] [ j ] [ k ]   ∣   d p [ i − 1 ] [ j ⊕ 1 ] [ k − A [ i ] ] dp[i][j][k]=dp[i-1][j][k]\ |\ dp[i-1][j\oplus 1][k-A[i]] dp[i][j][k]=dp[i1][j][k]  dp[i1][j1][kA[i]] 实现上, D P DP DP 数组可以压缩为 O ( M ) O(M) O(M)

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 10005
#define maxm 100005
int N, M, A[maxn];
bool dp[2][maxm];

int main()
{
    while (~scanf("%d%d", &N, &M) && (N | M))
    {
        for (int i = 0; i < N; ++i)
            scanf("%d", A + i);
        memset(dp, 0, sizeof(dp));
        dp[0][0] = 1;
        for (int i = 0; i < N; ++i)
        {
            for (int j = M; j >= A[i]; --j)
            {
                dp[0][j] |= dp[1][j - A[i]];
                dp[1][j] |= dp[0][j - A[i]];
            }
        }
        int res = 0;
        for (int i = 1; i <= M; ++i)
        {
            if (dp[1][i] && !dp[0][i])
                ++res;
        }
        printf("%d\n", res);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值