[博弈论]取石子(困难)

这篇博客探讨了一个基于记忆化搜索的策略问题,当堆石子的操作数为奇数时,先手玩家如何确保胜利。文章通过递归函数dp,分析不同情况下的后续状态,包括合并堆和减少操作数等,最终确定必胜策略。边界条件的处理确保了问题的完整解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://www.acwing.com/problem/content/1323/

在这里插入图片描述
思路:
感觉一定要很聪明脑洞大开的那种才能想出来

记忆化搜索

先考虑一种相对简单的情况。
假设题目给出的所有堆的个数都大于1。
设 总操作数 b = 堆数 + 石子总数 - 1,想到 b 为 奇数的时候 先手必胜。
在这里插入图片描述

简单情况时,始终保证所有堆的个数大于1,若某堆的石子个数=1了,则将这堆和别的堆合并,给到对面选手的状态,还时保持了所有堆的个数大于1.

设石子个数为1的堆有a个
定义 f ( a , b )
① 后继状态,从a中拿一个,f(a-1,b),如果是sg = 0的话,代表当前是必胜态。
② 后继状态,从>1的堆中拿一个,f(a,b-1),操作数减1,如果sg=0的话,代表当前是必胜态
③ 后继状态,两个>1的堆合并,f(a,b-1),操作数减1,如果sg=0的话,代表当前是必胜态
④ 后继状态,两个=1的堆合并,f(a-2,b+(3 或 2) ,正常是3,如果b=0的话等于2,因为定义时是-1,如果b本身有值,就代表已经减过了不用减了。
④ 后继状态,一个=1的堆和>1的堆合并,f(a-1,b+1),b中某个堆多了1。若sg=0,则当前必胜。

处理边界:
如果a = 0,则就回到上面的简单情况 b为奇数先手必胜
如果b = 1,则代表当前只剩1堆了,并且这堆里只剩下1,所以返回dp(a+1,0),a中多了一堆,b中没有堆了。

#include<bits/stdc++.h>
using namespace std;
const int N = 55,M = 50050;

int f[N][M];
int t,n;
int dp(int a,int b){
    int &v = f[a][b];
    if(v != -1) return v;
    if(a == 0) return v = b % 2;
    if(b == 1) return v = dp(a+1,0);
    
    if(a && !dp(a-1,b)) return v = 1;
    if(b && !dp(a,b-1)) return v = 1;
    if(a >= 2 && !dp(a-2,b + (b?3:2))) return v = 1;
    if(a && b && !dp(a-1,b+1)) return v = 1;
    
    return v = 0;
}
int main(){
    scanf("%d",&t);
    memset(f,-1,sizeof f);
    while(t--){
        scanf("%d",&n);
        int a = 0,b = 0;
        for(int i=1;i<=n;i++){
            int x;
            scanf("%d",&x);
            if(x == 1) a++;
            else b += b? x+1:x;
        }
        if(dp(a,b)) puts("YES");
        else puts("NO");
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值