题意:
n个数ai组成的序列,在这n个数中选x个数,保证任意k个相连的数有1个数被选中。求这n个数中选x个数的最大和。
n <= 5000。
题解:
1.dp[i][j]表示前i个数选j个数且其中包括第i个数的最大和。
2.dp[i][j] = max(dp[m][j-1] + a[j]) , m∈[max(0 , i - k) , i)。
3.这道题因为n的原因O(n^3)的方法过不了,得把第三层循环变成一个单调队列,这样复杂度就降为了O(n^2)。
因为dp[m][j - 1]中m是不确定的,j-1是确定的,所以第一层循环变为j,第二层循环变成i。
4.举个例子:假如当前在计算dp[5][j],双向队列内有2,3,4,2符合题意正在用,而dp[4][j-1]<dp[5][j-1]那么4从队尾出队。
#include<bits/stdc++.h>
#define N 5005
using namespace std ;
int n , k , x , m ;
long long dp[N][N] ;
deque <int> q ;
int main()
{
int i , j , k ;
long long ans = 0 ;
long long a[N] ;
scanf("%d%d%d" , &n , &k , &x) ;
for(i = 1 ; i <= n ; i ++)
scanf("%lld" , &a[i]) ;
for(i = 0 ; i <= n ; i ++)
for(j = 0 ; j <= n ; j ++)
dp[i][j] = -1e18 ;
dp[0][0] = 0 ;
for(j = 1 ; j <= x ; j ++)
{
q.push_back(0) ;
for(i = 1 ; i <= n ; i ++)
{
while(!q.empty() && q.front() < i - k)
q.pop_front() ;
dp[i][j] = dp[q.front()][j - 1] + a[i] ;
while(!q.empty() && dp[q.back()][j - 1] <= dp[i][j - 1])
q.pop_back() ;
q.push_back(i) ;
}
q.clear() ;
}
ans = -1 ;
for(i = max(x , n - k + 1) ; i <= n ; i ++)
ans = max(ans , dp[i][x]) ;
if(ans < 0)
ans = -1 ;
printf("%lld" , ans) ;
}