【Codeforces 731E】 Funny Game【dp】

本文解析了一道涉及博弈论和动态规划的游戏问题。两人轮流从数列左侧取数,目标是使自己的得分与对手得分的差值最大化。通过逆向DP的方法给出了问题的有效解决方案。

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

题意:有一串长度为m数列,两个人轮流从左边开始拿数字,每次可以拿k(2-m)个数字,然后这人会获得相当于这k个数之和的score,然后这k个数变成一个数,也即他们的和在数列的最左边,两个是都想最大化自己和他人的差距,也即是的score(自己的)-score(对面的)最大,然后游戏当只剩下一个数的时候停止

题解:

这个和之前的一题很像http://blog.youkuaiyun.com/sjtsjt709/article/details/53326892

dp[i]记录1-i已经被合并的情况下,当前取数的人先手所能取得的和另一人的最大差值

很明显dp[i]=max(sum[j]-dp[j])(i<j<=n)

因为当前人拿多少个数显然是可以由自己决定的

然后记忆化搜索或者从后往前dp,顺带记录max(sum[j]-dp[j])(i<j<=n)

吐槽:

当时我做这题的时候这样考虑

dp[i][0]代表第一个人取完时 [1..i]合并了 score[0]-score[1]的最大值

dp[i][1]代表第二个人取完时 [1..i]合并了 score[1]-score[0]的最大值
dp[i][0]=max(sum[i]-dp[j][1])(1<=j<i)
dp[i][1]=max(sum[i]-dp[j][0])(1<=j<i)
最后ans=max(dp[n][0],-dp[n][1])

其实本质上这个方程是错的,但我还是看了很久

错的原因是:我取数的时候取得是max(sum[i]-dp[j][1]),然而这是建立在对方会取到j,使得[1..j]合并的情况下,也就是我想当然了

但双方都是聪明人,都 play optimally,所以这个方程本质上就是错的

所以这种类似博弈,双方都取最优解 play optimally的情况,一般是从后往前推

#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define PB push_back
#define MP make_pair
#define ll long long
#define MS(a,b) memset(a,b,sizeof(a))
#define LL (rt<<1)
#define RR (rt<<1|1)
#define lson l,mid,LL
#define rson mid+1,r,RR
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lb(x) (x&(-x))
void In(){freopen("in.in","r",stdin);}
void Out(){freopen("out.out","w",stdout);}
const int N=2e5+10;
const int M=3e5+10;
const int Mbit=1e6+10;
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
int dp[N],a[N],s[N];
int main()
{
    int n;
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
        int mx=s[n];
        for(int i=n-1;i>=1;i--){
            dp[i]=mx;
            mx=max(mx,s[i]-dp[i]);
        }
        printf("%d\n",dp[1]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值