PAT 甲级 1007 Maximum Subsequence Sum dp

地址 https://pintia.cn/problem-sets/994805342720868352/problems/994805514284679168

题目大意

给予一个整数N 

下一行再给予N个整数 空格隔开

求上述数字中连续的和最大是多少 并且输出连续最大和的起点和终点的数字 空格隔开

另外如果给予的N个整数都是负数则 连续最大和定义为0  输出N个整数的起点和终点的数字 空格隔开

0 <= N <= 10000

Sample Input:
10
-10 1 2 3 4 -5 -23 3 7 -21
Sample Output:
10 1 4

 

解答

本题使用动态规划

我们假设和最大的连续子序列的结尾是 num[i]。 那么这个和最大的连续子序列就有两种构成可能

1  num[x],num[x+1]...num[i-1],num[i]的和    num[x],num[x+1]...num[i-1]的和+num[i] 更大 比如 序列   1,2,3,  9,是以9结尾的连续子序列  和为15

2  num[i]     num[i]更大  比如  序列  -1,-2,-3, 9    和最大的连续子序列并且以9结尾就是9单个数字

情况1中 1+2+3 又可以认为是 以num[i-1]为结尾 的最大连续子序列的和 ,

如果我们定义dp[i] 是以num[i]为结尾的最大连续子序列的和 那么

上面情况1 2 可以归纳成状态方程 dp[i] = max(dp[i-1]+num[i],num[i])

这样就解决了 求和最大的连续子序列的和的问题。

 

连续子序列的边界L R的求得如下

因为dp[i] 表示以num[i]结尾的 和最大的连续子序列

那么右边界肯定是R =i , 左边界如果是情况1 则等于dp[i-1]的左边界   如果是情况2 那么L =i

#include <iostream>

using namespace std;

const int N = 10010;

int n;
int arr[N];

struct DP{
    long long val;
    int l;int r;
}dp[N];

int main()
{
    cin >> n;
    int allneg = 1;
    for(int i = 1 ;i <= n ;i++){
        cin >> arr[i];
        if(arr[i] >= 0) allneg=0;
    }
   
    if(allneg == 1){
        cout << 0 << " " << arr[1] << " " << arr[n] << endl;
        return 0;
    }
    dp[0].l = 1; 
    for(int i = 1;i <=n;i++){
        dp[i].l = dp[i-1].l;
        dp[i].val = max(dp[i-1].val+arr[i],(long long) arr[i]);
        dp[i].r = i;
        if(dp[i-1].val+arr[i] < arr[i]){ dp[i].l = i; }
    }
    
    struct DP ans = dp[1];
    for(int i = 1; i <=n;i++){
        if(ans.val < dp[i].val){
            ans = dp[i];
        }
    }
    
   cout << ans.val << " " << arr[ans.l] <<" " << arr[ans.r] <<  endl;
    
   
    return 0;
}

 

 

posted on 2021-02-11 00:16  itdef  阅读(73)  评论(0编辑  收藏  举报

导航