codeforces 737 D. Financiers Game 博弈 动态规划 记忆化搜索

codeforces 737  D

题意: A和B两个人玩游戏,有一个数组a,两个人轮流取数,A先,A只能从左往右取,B只能从右往左取,并且如果前一个人取了k个数,那么接下来的那个人就得取k或者k+1个数,如果剩下的数不足k个,那么游戏结束,输出  “A取出的值的和”  减去 “B取出的数的和” 的差。A想让这个差尽可能的大,B想让这个差尽可能的小,两个人都采取最优解,问最后的差是多少。

在一篇博客上看到一句话:

这种两人博弈一般都可以用两个dp写, 一个dp描述第一个人的最优态, 第二个dp描述第二个人的最优态,难点在于优化空间。

思路:参考下面这篇博客,我也是看这个看懂的。

http://www.itdaan.com/blog/2017/05/31/e51a6afa17ed501cd1e1a7d84fc98101.html

AC code:

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
typedef long long ll;
const int N=100010;
using namespace std;
int n,a[4002];
int dp[4002][100][100][2];
bool vis[4002][100][100][2];
int sum[4002];
int solve(int l,int cnt,int pre,int cur)    
{
    /*
    l表示A已经拿走了l个数
    cnt表示B比A多拿了cnt个数
    pre表示上一个人上次拿了pre个数
    如果cur==0表示这次该A拿了,cur==1表示这次该B拿了
    */
    if(vis[l][cnt][pre][cur])
        return dp[l][cnt][pre][cur];
    vis[l][cnt][pre][cur]=true;
    int r=l+cnt;
    if(n-l-r<pre)
        return dp[l][cnt][pre][cur]=sum[l]-(sum[n]-sum[n-r]);
    int ret=0;
    if(cur==0)
    {
        ret=solve(l+pre,cnt-pre,pre,1);
        if(n-l-r>pre)
            ret=max(ret,solve(l+pre+1,cnt-pre-1,pre+1,1));
    }
    else
    {
        ret=solve(l,cnt+pre,pre,0);
        if(n-l-r>pre)
            ret=min(ret,solve(l,cnt+pre+1,pre+1,0));
    }
    return dp[l][cnt][pre][cur]=ret;
}
int main()
{
    scanf("%d",&n);
    sum[0]=0;
    for(int i=1;i<=n;i++)
        {scanf("%d",&a[i]);sum[i]=sum[i-1]+a[i];}
    mem(vis,false);
    int ans=solve(0,0,1,0);
    printf("%d",ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值