1 条题解

  • 0
    @ 2026-1-27 21:45:13
    #include<bits/stdc++.h>
    using namespace std;
    
    // [1] n-财宝的件数;arr数组-存储每件财宝的价值(下标1~n);brr数组-前缀和数组,brr[i]表示前i件财宝的价值和
    int n,arr[5100],brr[5100]={};
    // [2] dp[l][r]-区间[l, r]内,当前玩家能获得的最大收益(双方均采取最优策略)
    int dp[5100][5100]={};
    
    int main(){
        // 问题分析:区间DP问题,两人轮流拿当前区间两端的财宝,均采取最优策略,求先手Jack的最大收益
        // 解题步骤:1.计算前缀和数组,快速获取任意区间的价值和;2.初始化长度为1的区间(只有1件财宝时,当前玩家直接获得);
        //          3.按区间长度从小到大动态规划:对于区间[l, r],当前玩家拿左或右后,剩下的区间由对方最优选择,当前玩家收益=区间和-对方在剩余区间的收益;
        //          4.最终dp[1][n]即为Jack的最大收益
        
        cin>>n;
        int sum=0;
        for(int i=1;i<=n;i++){
            cin>>arr[i];
            brr[i]=brr[i-1]+arr[i];  // [3] 计算前缀和:前i件财宝的价值和 = 前i-1件的和 + 当前件的价值
        }
        
        // 1. 初始化:长度为1的区间(只有1件财宝),当前玩家直接获得该财宝的价值
        for(int i=1;i<=n;i++) dp[i][i]=arr[i];
    
        // 区间DP核心循环:按区间长度从小到大计算(len为区间长度,从2到n)
        for(int len=2;len<=n;len++){
            // 遍历所有长度为len的区间的左端点l
            for(int l=1;l+len-1<=n;l++){
                int r=l+len-1;  // [4] 计算当前区间的右端点r
                // 2. 计算当前区间[l, r]的总价值
                int ans=brr[r]-brr[l-1];
                // 状态转移:当前玩家有两种选择(拿左端点l或右端点r)
                // 拿左端点后,剩下的区间是[l+1, r],对方在该区间的最优收益为dp[l+1][r]
                // 拿右端点后,剩下的区间是[l, r-1],对方在该区间的最优收益为dp[l][r-1]
                // 当前玩家的收益 = 区间总价值 - 对方在剩余区间的最优收益(因为对方会最大化自己的收益,即最小化当前玩家的收益)
                dp[l][r]=ans-min(dp[l][r-1],dp[l+1][r]); 
            } 
        } 
        cout<<dp[1][n];  // [5] 输出整个区间[1, n]的最优收益,即Jack的最大收益
        return 0;
    }
    
    • 1

    信息

    ID
    1346
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者