1. 程式人生 > >【Codeforces 738F】 Financiers Game【記憶化DP】

【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);

    }
}