子序列之和系列

一、一段最长连续子序列之和问题:

  给定一个数字序列:A1,A2 …… An,求i,j(i<=j<=n),使得Ai + Ai+1 + …… + Aj最大,输出最大和。
  样例:-2 11 -4 13 -5 -2
  输出:20
子问题:取一段连续子序列,要求是该序列和最大
思路:用dp[i]表示 1到 i 的最大序列和,而dp[i]的值与dp[i-1]和A[i]有关,dp[i] = max(dp[i-1]+A[i],A[i]);

	memset(dp,-inf,sizeof(dp));
	dp[0] = a[0];
	for(int i=1;i<n;i++){
		dp[i] = max(dp[i-1]+A[i],A[i]);
	}
	int ans = dp[0];
	for(int i=1;i<n;i++){
		if(dp[i]>ans){
			ans = dp[i];
		}
	}
	cout<<ans<<endl;	

二、两段最长连续子序列之和问题

    POJ 2479 Maximum sum
Description
  Given a set of n integers: A={a1, a2,…, an}, we define a function d(A) as below:
在这里插入图片描述
  Your task is to calculate d(A).
Input
  The input consists of T(<=30) test cases. The number of test cases (T) is given in the first line of the input.
Each test case contains two lines. The first line is an integer n(2<=n<=50000). The second line contains n integers: a1, a2, …, an. (|ai| <= 10000).There is an empty line after each case.
Output
  Print exactly one line for each test case. The line should contain the integer d(A).
Sample Input
  1
  10
  1 -1 2 2 3 -3 4 -4 5 -5
Sample Output
  13
Hint
  In the sample, we choose {2,2,3,-3,4} and {5}, then we can get the answer.
  Huge input,scanf is recommended.
题意:给出一组数列,求出数列中最大的两个不相交的子段和。
思路:对于每一个 i 来说,都有两段:[0, i ) 和 [i, n ) 。我们分别求出每个 i 对应这两段的最大子段和,然后遍历所有的i,得到最大值。

lp[1]=a[1];
	for(int i=2;i<=n;i++)
		lp[i]=max(a[i],lp[i-1]+a[i]);

此时得到的 lp[i] 是包含i的最大子序列和。

for(int i=2;i<=n;i++)
	lp[i]=max(lp[i-1],lp[i]);

此时 lp[i] 为i左边(包含i这个点)的最大子序列和。

AC代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
const int inf = 0x3f3f3f3f;
const int maxn = 50010;
using namespace std;
int lp[maxn],rp[maxn];
int a[maxn];
int main(void)
{
    int t;
    cin>>t;
    while(t--){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",a+i);
        }

        lp[1]=a[1];
        for(int i=2;i<=n;i++)
            lp[i]=max(a[i],lp[i-1]+a[i]);
        for(int i=2;i<=n;i++)
            lp[i]=max(lp[i-1],lp[i]);
        rp[n]=a[n];
        for(int i=n-1;i>=1;i--)
            rp[i]=max(a[i],rp[i+1]+a[i]);
        for(int i=n-1;i>=1;i--)
            rp[i]=max(rp[i+1],rp[i]);

        int ans = -inf;
        for(int i=2;i<=n;i++){
            ans = max(ans,lp[i-1]+rp[i]);
        }
        printf("%d\n",ans);

    }
    return 0;
}

三、m段最长连续子序列之和问题

[HDOJ 1024 Max Sum Plus Plus](http://acm.hdu.edu.cn/showproblem.php?pid=1024)
Problem Description
  Now I think you have got an AC in Ignatius.L’s “Max Sum” problem. To be a brave ACMer, we always challenge ourselves to more difficult problems. Now you are faced with a more difficult problem.
Given a consecutive number sequence S1, S2, S3, S4 … Sx, … Sn (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx ≤ 32767). We define a function sum(i, j) = Si + … + Sj (1 ≤ i ≤ j ≤ n).
  Now given an integer m (m > 0), your task is to find m pairs of i and j which make sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + … + sum(im, jm) maximal (ix ≤ iy ≤ jx or ix ≤ jy ≤ jx is not allowed).
  But I`m lazy, I don’t want to write a special-judge module, so you don’t have to output m pairs of i and j, just output the maximal summation of sum(ix, jx)(1 ≤ x ≤ m) instead.
Input
  Each test case will begin with two integers m and n, followed by n integers S1, S2, S3 … Sn.
Process to the end of file.
Output
  Output the maximal summation described above in one line.
Sample Input
  1 3 1 2 3 2
  6 -1 4 -2 3 -2 3
Sample Output
  6
  8
Hint
  Huge input, scanf and dynamic programming is recommended.
题意:给出一组数列,求出数列中最大的m个不相交的子段和。
思路:题解参考自https://www.cnblogs.com/dongsheng/archive/2013/05/28/3104629.html动态规划的思想
1.基本思路:
  首先,定义数组a[n],dp[m][n];
  a[n]用来储存n个整数组成的序列;
  dp[i][j]用来表示由前 j 项得到的含 i 个子段的最大值,且最后一个子段以a[j]结尾。可以得出:
  因为必须是以a[j]结尾,所以a[j]一定是属于最后一个子段,既要么自己独立成一个子段,要么与前边以a[j-1]为结尾的子段连接
  所以dp[i][j] = max(dp[i][j-1]+a[j],dp[i-1][t]+a[j]) 其中 i-1 <= t <= j-1.
  所求的最后结果为max(dp[m][j])其中1<=j<=n;
  但是我们会发现,当n非常大时,这个算法的时间复杂度和空间复杂度是非常高的,时间复杂度近似为O(mn2),空间复杂度近似为O(mn)。因此,我们需要优化算法来降低时间复杂度和空间复杂度


2.优化算法:
(1)节省时间
  由基本思路,我们可以知道,dp[i][j] = max(dp[i][j-1]+a[j],dp[i-1][t]+a[j]) 其中 i-1 <= t <= j-1.我们只要找到dp[i][j-1]和dp[i-1][t]的最大值,再加上a[j]就得到dp[i][j]。
  所以定义一个数组mdp[n],用mdp[j-1]来表示求解dp[i][j]时,dp[i-1][t]的最大值,则dp[i][j] = max(mdp[j-1],dp[i][j-1]) + a[j];
  特别注意,mdp[n]这个位置的储存空间是始终用不到的,因此可以用来储存其他数值,在接下来会用到。
  在求解dp[i][j]的同时,我们可以计算出dp[i-1][t],(i<=t<=j)的最大值,这个最大值在计算dp[i][j]的时候需要做为mdp[j]的形式用到,我们先把它存放在mdp[n]中。
  你可能会问:为什么不把它直接存放在mdp[j]中呢?因为你接下来需要计算dp[i][j+1]的值,需要用到mdp[j]中原来的值。如果你把它存在这里就会覆盖计算dp[i][j+1]所需要的值。所以把它放在mdp[n]中。
  当我们计算完dp[i][j+1]后,就会发现mdp[j]中的值已经没有用处了,我们可以把它更新为计算dp[i+1][j+1]所需要的那个值,即之前放在mdp[n]中的那个值,即执行mdp[j]=mdp[n].这样我们就节省了计算最大值时付出的时间代价。
(2)节省空间
  通过时间的节省,我们突然间发现程序执行结束后mdp[n]的值即为最后的结果,mdp[n]数组才是我们希望求解的,dp[m][n]这个庞大的数组已经不是那么重要了,因此,我们现在用整型数ans来代替dp[m][n],用来临时存储dp[i][j]的值,作为求解mdp[n]的中介。这样就节省了dp[i][j]占用的极大的空间.

AC代码:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
const int maxn = 1000005;
using namespace std;
int n, m;
int a[maxn],mdp[maxn];
int DP()
{
    for(int i=1;i<=m;++i){

        int ans = 0;
        for(int k=1;k<=i;++k)
            ans += a[k];
        mdp[n] = ans;    //初始化

        for(int j=i+1;j<=n;++j){
            ans = max(mdp[j-1],ans) + a[j];
            mdp[j-1] = mdp[n];
            mdp[n] = max(mdp[n],ans);
        }
    }
    return mdp[n];
}
int main()
{
    while(~scanf("%d%d", &m, &n)){
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            mdp[i] = 0;
        }
        printf("%d\n", DP());
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逃夭丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值