hdu1024 (滚动数组和动态更新优化)

题意: 给定长度为 n n 的序列 a a ,从中选取 m m 个子段,求子段和的最大值。
题解:
f [ i ] [ j ] f[i][j] 表示将前 j j 个元素分成 i i 段可以获得的最大 i i 段和,其中这里必然包括第 j j 个元素。
状态转移为 f [ i ] [ j ] = m a x ( f [ i ] [ j 1 ] + a [ j ] , f [ i 1 ] [ k ] + a [ j ] ) , k [ i 1 , j 1 ] f[i][j]=max(f[i][j-1]+a[j],f[i-1][k]+a[j]),k\in[i-1,j-1]

  • f [ i ] [ j 1 ] + a [ j ] f[i][j-1]+a[j] 表示第 j j 个元素划分到第 i i 段,并将前 j 1 j-1 个元素分成 i i 段的最大和
  • f [ i 1 ] [ k ] + a [ j ] , k [ i 1 , j 1 ] f[i-1][k]+a[j],k\in[i-1,j-1] ,表示将第 j j 个元素划分到第 i 1 i-1 段,且将前 k k 个元素划分成 i 1 i-1 段可以获得的最大和。

这里的时间复杂度和空间复杂度必然爆炸,考虑优化。

  • 首先, f [ i ] [ j ] f[i][j] 只和前一个状态 i 1 i-1 有关,故可以用滚动数组优化。
  • 其次,枚举 k k O ( n ) O(n) 的时间复杂度,考虑动态更新 f [ i 1 ] [ k ] f[i-1][k] 的最大值,这里每一层的 f [ i 1 ] [ k ] f[i-1][k] 也只对下一层有影响,故也可以滚动数组优化。
  • 所以这里记: f [ j ] f[j] 表示前 j j 个元素(必然包括 s [ j ] s[j] ),构成最多 j j 个子段可以得到的最大和, p r e [ i ] pre[i] 表示前 i i 个元素中可以得到的最大和。

代码:

#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<iostream>
using namespace std;

const int N = 1e6 + 10;
int m, n;
int s[N];
int pre[N], f[N];

int main()
{
	while(~scanf("%d%d", &m, &n)) {
		for(int i = 1; i <= n; i++) pre[i] = 0, f[i] = 0;
		for(int i = 1; i <= n; i++) scanf("%d", &s[i]);
		
		int temp = 0;
		for(int i = 1; i <= m; i++) {
			temp = -0x7fffffff; //temp动态更新前j-1元素分成i-1段的最大值 
			for(int j = i; j <= n; j++) {
				f[j] = max(f[j - 1], pre[j - 1]) + s[j]; //f[j-1]表示将j划到i段,pre[j-1]表示将j划到i-1段 
				pre[j - 1] = temp; //更新为第i+1层,这里的temp是j-1时的 
				temp = max(temp, f[j]);
			}
		}
		//由于f[n]表示必然选第n个元素,但实际答案未必。 
		printf("%d\n", temp);
	}
}

注意: 为了保证分成 i i 个子段至少要有 i i 个元素,枚举元素数要从子段数开始。

猜你喜欢

转载自blog.csdn.net/weixin_43900869/article/details/107634524
今日推荐