前缀和复习:
设数组是a[]
s[i]=a[0]+……+a[i]
第i位到第j位的和为
a[i]+a[i+1]+……+a[j]
=(a[0]+……+a[j])-((a[0]+……+a[i-1])
=s[j]-s[i-1]
例如:给定n个数字a[1],a[2],a[3]……a[n],请你在其中取出任意段连续的数字,每段长度为k,要求选取的连续数字不能重复。求最大数字和。
对于这个问题,我们很容易能定义状态dp[i],表示当i为最后一段结尾时最大的数字和。此时,状态转移方程是:
dp[i]=max(dp[1],dp[2],……dp[i-1])+(a[i-k+1]+a[i-k+2]+……+a[i])
当1<=i<k时,无法取出这段连续的k个数字的,那么这个动态规划问题的边界条件是
dp[i]=0,1<=i<k.
那么我们应该如何优化取max和求和的这两部分呢?
首先,对于求和的部分,可以定义前缀和sum[i]=a[1]+a[2]+……+a[i],这样求和部分变化为
a[i-k+1]+a[i-k+2]+……+a[i]=sum[i]-sum[i-k]
至于maxx[i]=max(dp[1],dp[2],……,dp[i-k])=max(mx[i-1],dp[i-k);
我们也可以认为mx本质上就是一个前缀和,只是其运算操作是max而不是+。
代码示例
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6+5;
int a[N], dp[N], sum[N];
int main()
{
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
int mx = 0;
for(int i = 1; i <= n; i++){
sum[i] = sum[i - 1]+ a[i];
if(i >= k){
mx = max(mx, dp[i - k]);
dp[i] = mx + sum[i] - sum[i - k];
}
}
for(int i = n - k + 1;i <= n; i++){
mx=max(mx, dp[i]);
}
cout << mx << endl;
return 0;
}`