HDU1024 Max Sum Plus Plus(滚动数组优化空间+dp空间换时间)

题意

输入n个数。选取m个不重叠的区间,使得m个区间内的数之和最大。

解题

要记录两个量:选取了的数的个数、选取的区间个数。故设dp[i][j]表示选取了i个不重叠区间、选取了j个数、最后一个区间包含第j个数的选取方式的值。
这样,dp[i][j]=max{dp[i][j-1],dp[i-1][k]}+s[j],k取值范围是[i-1,j-1]。
空间复杂度为O(n*m),时间复杂度为O(n*m*n)。

因为第i行的信息更新只需要用到第i-1行的信息,故可以通过滚动数组来优化空间,将二维数组降成一维。空间复杂度降为O(n)。
对于上式中dp[i-1][k],k取值范围是[i-1,j-1]。
设t[i][j]表示第i行第i-1列的值到第j-1列的值的最大值。同样用滚动数组维护,即设tmp[j]表示当前行[i-1,j-1]的最大值。
这样,通过dp预处理出dp[i-1][k]。时间复杂度降为O(n*m)。

AC代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const ll maxn=1e6+100;
//ll dp[maxm][maxn];//选取第j个,前j个选i组的最大和
//dp[i][j]=max(dp[i][j-1],dp[i-1][k])+s[j].i-1<=k<=j-1
ll s[maxn],m,n;
/*
void solve() //朴素实现
{
    for(ll i=0;i<=n;i++) dp[0][i]=0;
    for(ll j=0;j<=m;j++) dp[j][0]=0;

    for(ll i=1;i<=m;i++)
        for(ll j=1;j<=n;j++)
    {
        for(ll k=i-1;k<=j-1;k++)
            dp[i][j]=max(dp[i][j],max(dp[i][j-1],dp[i-1][k])+s[j]);
    }
    ll ans=-INF;
    for(ll i=m;i<=n;i++)
        ans=max(ans,dp[m][i]);
    prllf("%lld\n",ans);
}
*/
ll d[maxn];//d[j]的含义与dp[i][j]的含义一样,循环的次数来记录i
ll tmp[maxn];//用tmp[j]表示二维数组dpi行区间[i,j]的最大值,注意推后更新
//d[j]=max(d[j-1],tmp[j-1])+s[j]
void youhua()//可以优化的部分有两点:滚动数组以及预先求出dp[i-1][k]
{
    for(ll i=0;i<=n;i++) d[i]=0;
    for(ll i=0;i<=n;i++) tmp[i]=0;
    ll now;
    for(ll i=1; i<=m; i++) //循环m次,那么最终的d[n]就等于dp[m][n]
    {
        now=-INF;//当前行[1,j]的最大值
        for(ll j=i; j<=n; j++)
        {
            d[j]=max(d[j-1],tmp[j-1])+s[j];
            //tmp[j-1]已用了,可以更新
            tmp[j-1]=now;
            now=max(now,d[j]);
        }
    }
    printf("%lld\n",now);
}
int main()
{
    while(~scanf("%lld%lld",&m,&n))
    {
        for(ll i=1;i<=n;i++)
            scanf("%lld",&s[i]);
        //solve();
        youhua();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37685156/article/details/80977688