HDU1024 Max Sum Plus Plus【最大子段和+滚动数组优化】⭐

Max Sum Plus Plus

题意: 从n个数中选取m个区段,使得和最大。今天脑子一直不在线,以至于看了题解也不明白是为什么。终于在发呆了亿会之后,终于回过神来看懂了。

题解: n的范围在1e6,而m的范围直接不告诉你了,所以想开二维的DP去做的话,后果你懂的。直接步入正题,怎么用一维的数组来写。

  • 定义状态dp[i]:题目中有一个条件,选取m个区段,如果只是单纯求最大的区段和,就简单得多,但是我们要刚好控制是m个区段,同时又是个一维数组。所以定义状态dp[i]表示前i个数中选取了i之后所能达到的最大和,之所以这样定义状态,使为了控制选取m个区段,i>=m时保证选取了至少m个区段。
  • 状态转移:为了使dp[i]最大,我们需要从dp[1]~dp[i-1]中选取一个最大值加上a[i](明确一点,dp[i]使选取了第i个数之后所能达到的最大值,并不代表在dp[1 ~ i]之间就一定最大。 )因此状态转移方程大致可以表示为:dp[i] = max{dp[1], dp[2], ...,dp[i - 1]} + a[i]
  • 定义pre[i]:作用无非就是记录下前i个dp中的最大值而已,不至于查找都重新遍历一遍。
  • 循环计算m次dp:dp毕竟只是一维的,跑一次不过只能确定一个区段的最大和而已,如果时m个区段,那我们就跑m次循环咯。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;

#define endl "\n"
#define dbg(x...)             \
    do {                      \
        cout << #x << " -> "; \
        err(x);               \
    } while (0)

void err() {	cout << endl;}
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) {
    cout << arg << ' ';
    err(args...);
}

inline int read(){
   int s = 0, w = 1;
   char ch = getchar();
   while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
   while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
   return s * w;
}

const int N = 1e6 + 10;
int n, m;
int maxdp;
int a[N];
int dp[N];
int pre[N];

void run() {
	for(int i = 1; i <= n; i++){
		a[i] = read();
		dp[i] = pre[i] = 0;
	}
	for(int i = 1; i <= m; i++) {
		maxdp = -inf;
		for(int j = i; j <= n; j++) {
			dp[j] = max(dp[j - 1], pre[j - 1]) + a[j];
			pre[j - 1] = maxdp;	//注意更新的是pre[j - 1],记录的是在第i次循环时,前j个dp的最大值,需要在i+1之后才被调用
			maxdp = max(maxdp, dp[j]);
			//可以试着在这里更新pre[j] = maxdp。结果会出错,读者需要自己思考琢磨一下
		}
	}
	printf("%d\n", maxdp);
}

int main() {
	while(~scanf("%d%d", &m, &n))	run();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值