HDU1024(动态规划+滚动数组)

题目大意:把一个数组划分成不相交的m段,使得这m段之和加起来最大。输出最大值。

这道题从题意的分析可知要使用动态规划来做
使用arr[j]来存放第j个数
dp[i][j]的含义:在包含arr[j]的前提下,前i段的最大值,那么我们可以分以下两种情况:

  • 1、arr[j]不是第i段的第一个数,那么此时dp[i][j]就是dp[i][j-1]+arr[j]
  • 2、arr[j]是第i段的第一个个数,那么此时dp[i][j]就是max(dp[i-1][k])+arr[j] (i-1<=k<=j-1)

解释一下为什么第二种情况要使用max(dp[i-1][k])+arr[j], i-1<=k<=j-1,首先,我们要知道dp[i][j]是表示在包含arr[j]的前提下,前i段的最大值,但是呢,如果arr[j]是负数,那么dp[i][j]肯定就不是表示前i段的最大和了,而前i段的最大和肯定是在之前出现的,同理dp[i-1][j-1]并不一定表示的前i-1段的最大和,但是前i-1段的最大和肯定是在i-1到j-1之间存在的,所以要使用dp[i-1][k], i-1<=k<=j-1
接下来我们就可以写出递推公式:

dp[i][j] = max(dp[i][j-1]+arr[j],max(dp[i-1][k])+arr[j]) i-1<=k<=j-1

但是上述的公式还有一个漏洞 也就是dp[m][n],dp[m][n]这个表示的时在包含arr[n]的前提下的最大值,但是我们前面分析过,包含arr[n]的不一定是最大的,所以说最后还要用一次max(dp[m][k]) m<=k<=n

上述的公式看起来已经能够解决这个问题了,但是其实还是不能解决,为什么? 由于题目中的m的范围并没有给出,而n的范围时1e6,所以按照上述方法做,最终肯定会爆内存
下面我们就采用滚动数组来进行空间优化:
首先我们来分析以下递推公式,递推公式中的dp[i-1][k] i-1<=k<=j-1,表示的是前面状态的最大和,而同时我们也不关心最大和到底是在哪一个位置取,那么我们就可以用pre[j-1]来表示前j-1个状态的最大和,此时递推公式就变成:
dp[i][j] = max(dp[i][j-1]+arr[j],pre[j-1]+arr[j])
我们会发现此时递推公式中只存在i了,那么此时我们就可以将i去除
最终的递推公式为:
dp[j] = max(dp[j-1]+arr[j],pre[j-1]+arr[j])

下面为AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

const int MAXN = 1e6+5;
int n,m;
int dp[MAXN];
int pre[MAXN];
int arr[MAXN];

int main()
{
	while(~scanf("%d%d",&m,&n))
	{
		memset(dp,0,sizeof(dp));
		memset(pre,0,sizeof(pre));
		for(int i=1;i<=n;++i)
			scanf("%d",&arr[i]);
		int maxNum;
		for(int i=1;i<=m;++i)
		{
			maxNum = -0x3f3f3f3f;
			for(int j=i;j<=n;++j)
			{
				dp[j] = max(dp[j-1]+arr[j],pre[j-1]+arr[j]);
				pre[j-1] = maxNum;
				maxNum = max(maxNum,dp[j]);
			}
		}
		printf("%d\n",maxNum);
	}
	return 0;
}
发布了33 篇原创文章 · 获赞 2 · 访问量 1724

猜你喜欢

转载自blog.csdn.net/weixin_42469716/article/details/102980304
今日推荐