P2642 双子序列最大和 P1121 环状最大两段子段和 (双向最长上升子序列的应用)

 P2642 双子序列最大和  

题目描述

给定一个长度为n的整数序列,要求从中选出两个连续子序列,使得这两个连续子序列的序列和之和最大,最终只需输出最大和。一个连续子序列的和为该子序列中所有数之和。每个连续子序列的最小长度为1,并且两个连续子序列之间至少间隔一个数。

输入输出格式

输入格式:

第一行是一个整数表示n。

第二行是n个整数表示整数序列。

输出格式:

一个数,两个连续子序列的序列和之和。

输入输出样例

输入样例#1:  复制
5
83 223 -13 1331 -935
输出样例#1:  复制
1637
输入样例#2:  复制
3
83 223 -13
输出样例#2:  复制
70

说明

对于30%的数据N<=100。

对于60%的数据有N<=10000。

对于100%的数据有N<=1000000。

数据保证运算过程不会超过long long(int64)。


#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAXN 1000100
int n;
int a[MAXN];
int dp1[MAXN],dp2[MAXN];
#define INF 0x3f3f3f3f
int ans_left[MAXN],ans_right[MAXN];
int ans=0;
int main()
{
    int i;
    scanf("%d", &n);
    memset(dp1,0,sizeof(dp1));
    memset(dp2,0,sizeof(dp2));
    int ans=-INF;
    ans_left[0]=-INF;
    ans_right[n+1]=-INF;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    for(i=1;i<=n;i++)
    {
        dp1[i]=max(dp1[i-1]+a[i],a[i]);
        ans_left[i]=max(ans_left[i-1],dp1[i]);//到i为止,最大的连续子段
    }
    for(i=n;i>=1;i--)
    {
        dp2[i]=max(dp2[i+1]+a[i],a[i]);
        ans_right[i]=max(ans_right[i+1],dp2[i]);
    }
    for(i=2;i<=n-1;i++)
    {
        ans=max(ans_left[i-1]+ans_right[i+1],ans);//因为每次都是最大,算间隔为2一定是小了,所以只要考虑间隔为1的情况,间隔为1就是相差为2
    }
    printf("%d\n",ans);
    return 0;
}



P1121 环状最大两段子段和

题目描述

给出一段环状序列,即认为 A_1A1 和 A_NAN 是相邻的,选出其中连续不重叠且非空的两段使得这两段和最大。

输入输出格式

输入格式:

第一行是一个正整数 N(N\le 2\times 10^{5})N(N2×105) ,表示了序列的长度。

第二行包含 NN 个绝对值不大于 1000010000 的整数 A_iAi ,描述了这段序列,第一个数和第 NN 个数是相邻的。

输出格式:

一个整数,为最大的两段子段和是多少。

输入输出样例

输入样例#1:  复制
7
2 -4 3 -1 2 -4 3
输出样例#1:  复制
9

说明

【样例说明】

一段为 33 。


/*(1)没形成环000111000111000
(2)成环111000111000111 */
//两种情况的处理
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAXN 1000100
int n;
int a[MAXN];
int dp1[MAXN],dp2[MAXN];
#define INF 0x3f3f3f3f
int ans_left[MAXN],ans_right[MAXN];
int ans1=-INF,ans2=INF;
int main()
{
    int i;
    int sum=0;
    scanf("%d", &n);
    memset(dp1,0,sizeof(dp1));
    memset(dp2,0,sizeof(dp2));
    int ans=-INF;
    ans_left[0]=-INF;
    ans_right[n+1]=-INF;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sum+=a[i];
    }
    for(i=1;i<=n;i++)                           //(1)没形成环000111000111000
    {
        dp1[i]=max(dp1[i-1]+a[i],a[i]);
        ans_left[i]=max(ans_left[i-1],dp1[i]);//到i为止,最大的连续子段
    }
    for(i=n;i>=1;i--)
    {
        dp2[i]=max(dp2[i+1]+a[i],a[i]);
        ans_right[i]=max(ans_right[i+1],dp2[i]);
    }
    for(i=1;i<=n-1;i++)
    {
        ans1=max(ans_left[i]+ans_right[i+1],ans1);//因为每次都是最大,算间隔为2一定是小了,所以只要考虑间隔为1的情况,间隔为1就是相差为2
    }
    memset(dp1,0,sizeof(dp1));
    memset(dp2,0,sizeof(dp2));
    memset(ans_left,0,sizeof(ans_left));
    memset(ans_right,0,sizeof(ans_right));
    ans_left[0]=INF;
    ans_right[n+1]=INF;
    for(i=1;i<=n;i++)                          //(2)成环111000111000111
    {
        dp1[i]=min(dp1[i-1]+a[i],a[i]);
        ans_left[i]=min(ans_left[i-1],dp1[i]);//到i为止,最大的连续子段
    }
    for(i=n;i>=1;i--)
    {
        dp2[i]=min(dp2[i+1]+a[i],a[i]);
        ans_right[i]=min(ans_right[i+1],dp2[i]);
    }
    for(i=1;i<=n-1;i++)
    {
        ans2=min(ans_left[i]+ans_right[i+1],ans2);//间隔为0的情况。
    }
    if(sum!=ans2)
    ans2=sum-ans2;         //考虑到特殊情况,全为负数的时候(取最小的时候所有数都可以取,sum0-ans2=0,而ans1=sum,为负,ans==0,错误,所以这步
    ans=max(ans1,ans2);
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xigongdali/article/details/81040192
今日推荐