https://www.luogu.org/problem/P2627
题意:给一个长度为nn的数组。现在让你选一些数,并且选的数中不能有在数组中长度超过kk的连续段。求选出的数的和的最大值。
分析:共三种解决方案。
Solution One
顺推.
令 dp[i][0] 表示在前 i 头奶牛中,选了第 i 头奶牛能获得的最大效率
dp[i][1] 表示在前 i 头奶牛中,不选第 i 头奶牛能获得的最大效率
显然可以得出以下转移方程 ( 注意 K 是题目给出的区间长度)
可以预处理出前缀和优化掉一重循环,转移方程就变成了如下所示
因为最终都要加上一个 Sum[i] ,所以可以把 Sum[i] 移到 max函数外,所以
这时候我们可以发现,要使 dp[i][1] 最大,我们就要让 dp[ j ][ 0 ]-Sum[ j ] 尽可能的大,所以只需要用一个单调队列维护长度为 K 的区间中 dp[ j ][ 0 ]-Sum[ j ]dp[ j ][ 0 ]−Sum[ j ] 的最大值就好了.
维护过程还要注意队列中初始就要设置一个val=0,ind=0 的元素表示不减少,然后每次都往单调队列里添加元素,而不是用 i >= K 后再往队列里加,否则会少掉不选前 K 个的最优解情况。
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cmath> 5 #include <cstring> 6 using namespace std; 7 8 typedef long long ll; 9 const int maxn = 1e6+7; 10 ll a[maxn]; 11 ll sum[maxn]; 12 ll dp[maxn][2]; 13 struct node{ 14 int ind; 15 ll val; 16 }que[maxn]; 17 18 19 int main(){ 20 int n,k; 21 scanf("%d%d",&n,&k); 22 for(int i=1; i<=n; i++){ 23 scanf("%d",a+i); 24 sum[i] = sum[i-1] +a[i]; 25 } 26 27 int head=0,tail=1; 28 que[0].val = 0; 29 que[0].ind = 0; 30 for(int i=1; i<=n; i++){ 31 dp[i][0] = max(dp[i-1][0],dp[i-1][1] ); 32 33 while(head<tail && que[head].ind<=i-k-1) {head++;} 34 while(head<tail && que[tail-1].val<(dp[i][0]-sum[i]) ) tail--; 35 que[tail].val = dp[i][0]-sum[i]; 36 que[tail++].ind = i; 37 38 dp[i][1] = que[head].val+sum[i]; 39 40 // printf("ans=%lld\n", dp[i][1]); 41 } 42 43 printf("%lld\n", max(dp[n][0],dp[n][1])); 44 }
Solution Two
Solution Three