【Codeforces 738F】 Financiers Game【记忆化DP】

题意:

有一个数列,两个人分别从两端开始取数字,左边的先取,第一次能取1个或2个,然后轮流取得时候,如果第一个取了k个,后一个人只能取k或k+1个,如果剩下的不够了则游戏终止,I想要最大化两个人取得数字之和的差距,Z想要最小化,两个人都选择最优操作

题解:

重点是空间时间复杂度

上述算法看起来是n3次的dp[L][R][K]

但实际上L最多只能达到2000多一点的样子(I多拿一次数字的情况下

K需要从一开始增加,从1开始增加到最大1,2,……,k sum=k*(k+1)/2 k<=sqrt(2*n)也就是90左右

在来看R,R代表的是Z先生拿到的位置,设d为I与Z取走数字数量之差,d=(n - r) - (l - 1) 取左右100就200吧

所以最终我们只需要dp[2100][200][100][2];

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>

using namespace std;
int n;
int a[4005];
int dp[2100][200][100][2];
bool used[2100][200][100][2];
int sum[4005];

int dfs(int l,int d,int k,int t)
{
    if(used[l][d][k][t]!=0)
        return dp[l][d][k][t];

        used[l][d][k][t]=1;
        int dis=d-100;
        int cntr=l+dis;
        int r=n+1-cntr;

        int tans;
        if(t==0)
        {
            if(l+k<r)
            {
                tans=dfs(l+k,d-k,k,1);
                if(l+k+1<r)
                {
                    tans=max(tans,dfs(l+k+1,d-k-1,k+1,1));
                }
            }
            else
            {
                tans=(sum[l]-sum[0])-(sum[n]-sum[r-1]);
            }
        }
        else
        {
            if(r-k>l)
            {
                tans=dfs(l,d+k,k,0);
                if(r-k-1>l)
                {
                    tans=min(tans,dfs(l,d+k+1,k+1,0));
                }
            }
            else
            {
                tans=(sum[l]-sum[0])-(sum[n]-sum[r-1]);
            }
        }
        return (dp[l][d][k][t]=tans);
}

int main()
{
    while(~scanf("%d",&n))
    {
        memset(used,0,sizeof(used));
        //cout<<dp[123][32][34][1]<<endl;
        sum[0]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }

        int ans=dfs(0,100,1,0);
        printf("%d\n",ans);

    }
}

猜你喜欢

转载自blog.csdn.net/c_czl/article/details/83477009