数组两端取数之和最大

题目描述

一个整数数组,两个人一次分别从左边或者右边拿走一个数,两个人足够聪明,求第一个人拿到数的最大和。

可以提交代码的题目链接

解题思路

首先想到的是贪心法,每次都取两端中的最大的数,但是很显然这是错的。例如以下的测试用例:

 2 6 8 3

贪心法会得到结果9但是正确答案应该是10。而贪心法没法解决全局最优的时候,我们一般就会采用动态规划来解决。

动态规划最重要的就是状态的表示和状态转移方程,那么根据数组的特点我们比较容易想到使用dp[i][j]来表示数组下标为i到j(i<=j)的元素做游戏得到的最优解。而如果dp[i][j]代表了A选手在i到j元素上的最优解,那么A选手下一步要么取走i元素,要么取走j元素。

由于B选手也是每次选择最优的解,那么dp[i+1][j]或者dp[i][j-1]就代表了选手B在i到j元素上的最优解(因为A取了一个数之后剩下的元素要么是i+1到j,要么是i到j-1)。

再考虑到i到j的数组之和是固定的,那么A的得分加上B的得分就等于i到j的元素之和,A的得分就等于这个固定值减去B的得分,因此A要想得分最高,就等价于使得B的得分最低。

因此状态转移方程如下:

// sum(i,j)表示下标为i到j的元素之和
dp[i][j] = sum(i,j) - min(dp[i-1][j], dp[i][j-1]);

有了状态转移方程,就不难写出程序了。

AC代码

#include <iostream>
using namespace std;

int main()
{
    int N; cin>>N;
    int data[N];
    for(int i=0;i<N;++i) cin>>data[i];
    int dp[N][N];
    // 初始化,如果只有一个元素,最优就是直接取该元素
    for(int i=0;i<N;++i){
        dp[i][i] = data[i];
    }
    // res[i][j]表示i到j的元素之和
    int res[N][N];
    for(int i=0;i<N;++i){
        res[i][i] = data[i];
        for(int j=i+1;j<N;++j){
            res[i][j] = res[i][j-1]+data[j];
        }
    }
    // 数组从小到大开始dp
    for(int d=1;d<N;++d){
        for(int i=0;i+d<N;++i){
            dp[i][i+d] = res[i][i+d] - min(dp[i][i+d-1], dp[i+1][i+d]);
        }
    }
    cout<<dp[0][N-1]<<endl;
    return 0;
}
posted @ 2020-03-25 21:02  wenxuanh  阅读(1453)  评论(0编辑  收藏  举报