cf 1077f2 基础dp + 单调双向队列

题意:

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) ;
}

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/87558098