HDU1024 Max Sum Plus Plus
题意:给你一个N个元素的数列,你需要从中选M个区间,区间之间不能有交叉,问选取的M个区间之和最大值是多少? (N是1e6的数量级)
分析
我们考虑一个数一个数的放,过程是怎么样的。如果当前是选取第i个区间,现在要尝试放入a[j],那么就会出现两种情况:
1.a[j]直接跟在第i个区间的后面,最开始相当于跟在空区间的后面,后来就是a[j+1]跟在a[j]的后面组成区间
2.找到j之前选择i-1个区间的最优值+新开一个区间[j,j].
假如现在是第i轮区间选择:
dp[j]: 当前轮第j个位置的两种选择情况的最大值
mx:实时保存本轮在第j个位置选择后的最优值,当前轮的最优值
best[j-1]:上一轮在区间[1,j-1]选取i-1个区间的最优值,best[]是一个滚动数组,保存当前轮的最优值在下一轮使用
遇到的问题
找到j之前选择i-1个区间的最优值应该怎么处理呢?
当我们进行一轮新区间的时候,我们要使用到上一轮的j之前的最优值,同时又要保存j之前选取新区间之后的最优值
第一轮区间选择,其j之前的最优值都是0,因为只选取了0个区间
当我们用了上一轮的best[j-1],紧接着就要更新下一轮的best[j-1],此处具体看代码
AC代码
#include <iostream> #include <algorithm> #include <cstring> using namespace std; const int maxn = 1e6+100; typedef long long ll; int M,N; int arr[maxn];//原数组 ll best[maxn],dp[maxn];//best上一轮的最优值,本轮dp[j]:本轮选择了j位置的最优值 int main(){ while(~scanf("%d %d",&M,&N)){ memset(best,0,sizeof(ll)*N+10); memset(dp,0,sizeof(ll)*N+10); ll mx; for(int i = 1;i<=N;i++) scanf("%d",&arr[i]); for(int i = 1;i<=M;i++){//轮次 mx = -1e18; for(int j = i;j<=N;j++){ dp[j] = max(dp[j-1],best[j-1])+arr[j];//选择j位置后的最优值,使用掉上一轮best[j-1] best[j-1] = mx;//保存本轮的j-1的最优值,放在下一轮用 mx = max(mx,dp[j]);//更新本轮j位置的最优值 } } printf("%lld\n",mx); } return 0; }