最大子段和之M子段和
Posted hbhszxyb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大子段和之M子段和相关的知识,希望对你有一定的参考价值。
最大M
子段和
题目模型
N
个整数组成的序列 (a_1,a_2,a_3,…,a_n) ,将这N
个数划分为互不相交的M
个子段,并且这M
个子段的和是最大的。
问题分析
-
方法一:
-
看到序列,我们首先要尝试用线性
dp
去处理,线性dp
经典状态定义:f[i][j]
,i
一般表示序列的前i
个元素,j
表示限制,这里表示划分了j
个不相交的子段,我们还需要对i
进行进一步的定义,即是否包含第i
项,因为对当前元素a[i]
来说,要么单独成一个子段,要么和最后一个子段合并,所以必须包含第i
个元素。 -
动态转移方程:
dp[i][j]=max(dp[i-1][j],dp[k][j-1])+a[i] (j-1<=k<i)
。 -
Code
#include <bits/stdc++.h> const int maxn = 1e3+3,Inf=0x3f3f3f3f; typedef long long LL; int a[maxn],dp[maxn][maxn]; void Solve(){ int n,m;scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=n;++i){//前i个元素 for(int j=1;j<=std::min(i,m);++j){//划分出j个子段 if(i==j)dp[i][j]=dp[i-1][j-1]+a[i];//显然 else{ int temp=dp[i-1][j];//把a[i]直接并到最后一子段 for(int k=j-1;k<i;++k)//枚举上一个状态的最后一个子段的右端点,a[i]单独作为一个子段 temp=std::max(temp,dp[k][j-1]); dp[i][j]=temp+a[i]; } } } int ans=-Inf; for(int i=m;i<=n;++i) ans=std::max(ans,dp[i][m]); printf("%d ",ans); } int main(){ Solve(); return 0; }
-
时间效率为:(O(n^3)) ,空间效率为:(O(m*n))。
-
-
方法二:
-
我们尝试对方法一的
dp
阶段和状态进行修改, 即把子段限制数M
作为阶段,即状态dp[i][j]
表示把序列前j
分成i
个子段且包含a[j]
的最大子段和。 -
动态转移方程有:
dp[i][j]=max(dp[i][j-1],dp[i-1][k])+a[j] (i-1<=k<j)
。-
dp[i][j-1]+a[i]
:表示合并到最后一个子段里 -
dp[i-1][k]+a[i]
:表示前k
元素挑出k
个子段,所以k>=j-1
,然后a[i]
单独的子段。 -
此动态转移方程同样满足无后效性和最优子结构。
-
我们把问题的所有状态记录下来形成一个二维矩阵,显然当前状态只跟它上一行和左边的状态有关,我们可以把空间效率压掉以为变成 (O(n)) 。
-
同时上一行的状态只有在当前状态前面的最大值对转移有用,我们可以在遍历当前行时维护一下上一行的最大值,这样时间效率就压掉了一个
n
,变成(O(n*m))。 -
Code
#include <bits/stdc++.h> typedef long long LL; const int maxn = 1e4+5; const LL Inf=0x3f3f3f3f3f3f3f3f; LL a[maxn],dp[2][maxn]; void Solve(){ int n,m;scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%lld",&a[i]); int k=1;//滚动数组指针,k表示当前行,!k表示上一行 for(int i=1;i<=m;++i,k=!k){//枚举区间个数 LL Max=-Inf; for(int j=i;j<=n;j++){ Max=std::max(Max,dp[!k][j-1]);//记录前j-1,分成i-1个区间时最大值 if(i==j) dp[k][j]=dp[!k][j-1]+a[j]; else//要么是a[j]单独成一个区间,此时为Max+a[j],或者直接合并为dp[k][j-1]+a[j] dp[k][j]=std::max(Max,dp[k][j-1])+a[j]; } } LL ans=-Inf; for(int i=m;i<=n;++i)//!k行才记录的是第m行的状态 ans=std::max(ans,dp[!k][i]); printf("%lld ",ans); } int main(){ Solve(); return 0; }
-
-
以上是关于最大子段和之M子段和的主要内容,如果未能解决你的问题,请参考以下文章