Max Sum Plus Plus 最大子段和 经典 Dp

本文探讨解决最大m子段问题的方法,通过动态规划实现高效的求解过程,特别关注如何在时间复杂度为O(mn)的情况下优化算法性能,以及在面对大规模数据集时如何运用滚动数组技术降低空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

kuangbin专题链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=68966#problem/A

题意:最大m子段问题:求给定无交叉的区间数目,求出最大和值。

看到,n达到1000000,心里第一想法就是只能是一维Dp问题,可是,在推导的状态转移公式时,还得用二维Dp思想。

看到网上的算法时间复杂度为O(mn),看来m不是很大。


其实,我们很容易想到一个状态转移方程:dp[i][j]:表示在第i个位置处,区间数为j的最大和值。

则有两种转移情况:

一:dp[i][j]中的第i个位置,不属于dp[i][j]中的最后一个子段j(值等于子段数目),则 dp[i][j]=dp[i-1][j];

二:dp[i][j]中的第i个位置属于dp[i][j]中的最后一个子段j(值等于子段数目),此时,我们又容易想到:最后一个子段的终点一定,枚举最后一个子段的起点即可,状态转移方程为:dp[i][j]=max(dp[k][j-1]+s[i]-s[k]),其中s表示前缀和,1<k<i。刚开始我就是这样想的,无奈做不出来。看题解:此时,又把最后一个位置j分成两种情况:

1:dp[i][j]的最后一个位置i属于dp[i-1][j]的最后一个子段j,即:相当于把位置i放入dp[i-1][j]的尾部,则:dp[i][j]=dp[i-1][j]+a[i]。

2:dp[i][j]的最后一个位置i不属于dp[i-1][j]的最后一个子段j,即:最后一个位置属于一个独立的子段,则:dp[i][j]=dp[k][j-1]+a[i]。1<=k<i;

至此转移方程就出来了,但是n是在太大了,开一个二位数组不太妥。说要用滚动数组,其实我对滚动数组还是没怎么会用,参考别人的代码,然后写了一遍。

//freopen("C:\\Documents and Settings\\All Users\\桌面\\in.txt","r",stdin);

#include<iostream>
#include<cstdio>
#include<sstream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<queue>
#include<vector>
#include<map>
#include<string>
#define LL __int64
#define INF 0x7fffffff

using namespace std;

int dp[1000010],p[1000010],a[1000010],m,n;

int main(){
    while(cin>>m)
    {
        cin>>n;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        memset(dp,0,sizeof(dp)),memset(p,0,sizeof(p));
        int Max;
        for(int i=1;i<=m;i++){
            Max=-INF;
            for(int j=i;j<=n;j++)//很显然,j必须>=i
            //滚动数组就是及时回收利用后面用不到的空间,这里是p数组
                dp[j]=max(dp[j-1],p[j-1])+a[j],p[j-1]=Max,Max=max(dp[j],Max);
        }
        cout<<Max<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值