hdu1024 Max Sum Plus Plus(最大子段和加强版)

关键字:dp   滚动数组

题意:输入n,m表示 一排n个数,求m个不相交的子段,他们和最大。

思路:果断dp啊,最大子段和的变形,联想最大子段和的dp[i] 设以当前i为结尾的子段和最大的结果,这里设dp[i][j]为以j结尾构成了i段不相交子段和的最大结果。

我们就要思考当前dp[i][j]是由那些状态转移过来?思考这里对于每个数字,我们的策略是要么选择他作为当前这段的,要么让它单独成段(对于这一段不一定要选),单独成一段的话,就又要从上一层的状态里dp[i-1][k],(表示选择第k个数字后已经形成I-1 个段落,i-1<=k<=j-1)里选择最优的那一个.

   所以分析后,他的状态转移方程是  dp[i][j]=max(dp[i][j-1]+a[j],max(dp[i-1][k])+a[j]);

这里有一个小技巧,对于这个状态转移方程式,很多菜鸟推出来就容易写成O(n^3)

对于查找dp[i-1][k] 中最大的那一个,我么可以用一个一维数组Max[j]保存上一轮选择到第j个数字前的最大结果,这样时间复杂度O(n*m)。

但是这题j的范围是10^6,i未知,时间肯定不会超的,因为想不到更优的转移方程。然而空间需要优化一点,利用转移方程式只与i.i+1有关,就用滚动数组,节省一维空间。

#include
using namespace std;
const int maxn=1000010;
const int inf=0x7fffffff;
int dp[maxn],a[maxn],Max[maxn],res;
int main()
{
    int n,m;
    while(~scanf("%d %d",&m,&n)){
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        memset(dp,0,sizeof(dp));
        memset(Max,0,sizeof(Max));
        for(int i=1;i<=m;i++){
            res=-inf;
            for(int j=i;j<=n;j++){
                dp[j]=max(dp[j-1],Max[j-1])+a[j];
                Max[j-1]=res;
                res=max(res,dp[j]);
            }
        }
        printf("%d\n",res);
    }
}


你可能感兴趣的:(动态规划基础dp)