HDU 1024 max sum plus plus

题意

给两个数m和n,然后再给出n个数。在1到n个数中从中选出m个区间,每个区间求和,然后再吧m个区间求和使得这个和最大。区间的要求是不允许与其他的区间有任何交集,一个数也不行,另一个要求是这个区间可以是一个数。输出是这个最大和

分析

动态规划问题,我们要在n个数中取m个区间和最大,那么可以看看最后一个数是怎么取的。假设取的最后一个数j使得这几个个区间和最大,那么这个区间和最大情况可以分为两个部分
 1.如果第j-1个数也取了,那么在取第j个数的时候j-1必须和n并为一个区间,那么也就是说,此时我想取i的话前j-1个数必须是取了m个区间且最大,那这样问题就划分为了跟他一样的子问题,即在j-1必须取的情况下从前j-1个数中选出m个区间使其区间和的总和最大
 2.如果第j-1个数没有取,那么第j个数要取的话就必须作为第m个区间,那也就是说我要在前j-1个数中取出m-1个区间,其区间和的总和最大。这个与子问题有两点不同,一是他求的是前j-1的m-1个区间,而不是m个,而是他的第j-1个不一定会取到。那么第一点表示我们要想求出取m个区间的话就必须知道m-1是个怎么样的情况,第二点表示我们必须遍历一次取m-1个区间的前j-1个数看取到哪个数的时候能使得他的区间和最大。
  综合上述分析,我们这样定义dp数组dp[i][j]:表示从前j个数中取出i个区间,且其区间和的总和最大那么通过上述分析可以得到递推方程dp[i][j]=max(dp[i][j-1]+a[j],max(dp[i-1][k])+a[j])其中k为1->j-1。一共m层,每层j个求k的时候还要循环,因此时间复杂度为O(n^3),超时。
  看第二种情况可以知道这里的max(dp[i-1][k])是上一层的最大值,那么我们可以在求dp[i-1][j]的时候顺便把这个最大值也求出来然后保存在maxn中所以只需要O(n^2)即可。那状态转移方程就是dp[i][j]=max(dp[i][j-1]+a[j],maxn[j-1]+a[j])
  但是m是未知的,n已经很大了,直接开个二维数组内存不够,还要优化。这里我们看这个状态转移方程可以看到,dp[i][j]的值只与1.跟他同一层的dp[i][j-1]和2.另外一个数组maxn[j-1]有关,因此我们可以用滚动数组。就是上一层的数用完以后就没有用了,所以可以直接把这一层的数据附上去。因此这里重新定义dp[j]:表示在第i层的时候前j个数取i对区间,其区间和的和最大。然后同样的状态转移方程也变成dp[j]=max(dp[j-1]+a[j],maxn[j-1]+a[j])
  问题到这里基本解决但是还有个小问题,就是在求出第i层前j个数的最大值的时候,我们也顺带求出了第i层maxn[j],然后在求第i层的dp[j+1]的时候需要用到第i-1层的maxn[j]。因此如果求出第i层的maxn[j]后不能直接赋值给当前也就是第i-1层的maxn[j]因为下一个要用。那么我们可以把这个最大值保存起来存到一个mmax变量中,等我用完了maxn的数据后再给他复回去。代码如下

#include <iostream>
#include <cstring>
using namespace std;
#define INF 0x7fffffff
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int num[100005];
    int dp[100005]; 
    int maxn[100005];
    int m,n;
    while (scanf("%d%d",&m,&n)!=EOF) 
    {
        int mmax;
        memset(num,0,sizeof(num));
        memset(dp,0,sizeof(dp));
        memset(maxn,0,sizeof(maxn));
        for (int i=1;i<=n;i++)
            scanf("%d",&num[i]);
        //以下为核心代码
        for (int i=1;i<=m;i++)
        {
            mmax=-INF;
            for (int j=i;j<=n;j++)
            {
                dp[j]=max(dp[j-1]+num[j],maxn[j-1]+num[j]);
                maxn[j-1]=mmax; //先把上次求出来的值赋回去
                mmax=max(mmax,dp[j]);//这一层求出来的先保存起来,等用完了上一层的在赋回去
            }
        }
        cout<<mmax<<endl;       
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/cugsl/article/details/79109742
今日推荐