1. 程式人生 > >最大m子段和

最大m子段和

必須 允許 ons scanf max 過程 序列 nbsp targe

參考自:最大m子段和總結與例題 51nod1052 HDU1024

題目介紹:

給定由n個整數(可能為負)組成的序列a1、a2、a3...,an,

以及一個正整數m,要求確定序列的m個不相交子段,使這m個子段的總和最大!

特別註意:

有些題目可能不存在負數答案,給出的序列全是負數,那麽不管m是多少,答案是0。此時選擇的子段是0個,不足m個,但符合題意。。。

也可能有些題目要求,必須選夠m個子段。

區別在dp數組的初始化。前者要求dp初始為0,後者要求第0行為0,其余為負無窮

解題思路:

動態規劃,借助矩陣可以直觀的看到計算過程。 定義二維數組dp, dp[ i ][ j ],表示前 j 項所構成 i 子段的最大和,且必須包含著第j項,即以第j項結尾 然後是一個遞推過程。 求dp[ i ][ j ],有兩種情況: 1、dp[ i ][ j ] = dp[ i ] [ j-1 ] + a[ j ] ,即把第j項融合到第 j-1 項的子段中,子段數沒變 2、dp[ i ][ j ] = dp[ i-1 ] [ t ] + a[ j ],(i-1<= t < j )

代碼:

#include<bits/stdc++.h>
using namespace std;  
typedef long long ll;
const int N=1e6+5; 
const int INF=0x3f3f3f3f;
int n,m;
ll a[N],dp[2][N];   //只保存上一行和當前行 
int main()  
{
    while(~scanf("%d%d",&n,&m))   //n個數字,m子段和 
    { 
        for(int i=1;i<=n;i++) 
            scanf("%lld",a+i);  
        
for(int i=0;i<=n;i++) dp[0][i]=0,dp[1][i]=0; //關鍵!此題答案只允許正值 for(int i=1,k=1;i<=m;i++,k^=1) //分為i段,k為兩行之間的切換 { dp[k][i-1]=-INF; //i==j時,杜絕與前一元素共伍 ll maxpre=-INF; //maxpre記錄上一行的最大值 for(int j=i;j<=n-m+i;j++) { maxpre
=max(maxpre,dp[k^1][j-1]); //隨時更新上一行最大值 dp[k][j]=max(dp[k][j-1],maxpre)+a[j]; //*對情況1、2的選擇
          //如果a[j]是負數的話,也是不會影響maxpre的。因為在第i子段中,a[j]的值是不斷變化的,總有dp[k][j]是當前最大值。
          //還得進一步加深對dp的理解。
} } ll ans=-INF; for(int i=m;i<=n;i++) //找到第m行的最大值,即為答案 ans=max(ans,dp[m&1][i]); printf("%lld\n",ans); } }

最大m子段和