codeforces940E 2000分dp

題目傳送門

題意:

n個數,第 i 個數是 a_i 。還有一個正整數 c 。

\dpi{150}n 個數分成任意段,每一段把段內最小的 \left \lfloor \frac{len}{c} \right \rfloor 個數去掉。 len 是該段內數的個數, c 是題目給定的數。

問剩餘數之和的最小值。

數據範圍:1 \leqslant n , c \leqslant 10^5 , 1\leqslant a_i \leqslant 10^9 。

題解:

dp[i] 表示前 i 個數分成任意段的最小值。

sum[i] 表示前 i 個數的和。

query(l,r) 計算 [l,r] 區間的最小值,ST表維護即可。

轉移方程:dp[i] = min(dp[i - 1] + a[i] , dp[i - c] + sum[i] - sum[i - c] - query(i - c + 1 , i)) 。

感受:

一眼dp,然後就沒有然後了。

這麼簡單的題越想越複雜,感覺總是想不到正確的思路上。

我好弱啊。

代碼:

#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 1e5 + 5 ;
int n , c , a[maxn] ;
int st[maxn][25] ;
ll sum[maxn] , dp[maxn] ;
void init()
{
   for(int i = 1 ; i <= n ; i ++) st[i][0] = a[i] ; 
   for(int j = 1 ; j <= 20 ; j ++)
   for(int i = 1 ; i + (1 << j) - 1 <= n ; i ++)
     st[i][j] = min(st[i][j - 1] , st[i + (1 << (j - 1))][j - 1]) ;
}
int query(int l , int r)
{ 
   int len = log2(r - l + 1) ;
   return min(st[l][len] , st[r - (1 << len) + 1][len]) ; 
}
int main()
{
	scanf("%d%d" , &n , &c) ;
	for(int i = 1 ; i <= n ; i ++)  scanf("%d" , &a[i]) ;
	sum[0] = 0 ;
	for(int i = 1 ; i <= n ; i ++)  sum[i] = sum[i - 1] + a[i] ;
	init() ;
	for(int i = 1 ; i <= n ; i ++)
	{
		ll y = 1e18 ;
		dp[i] = dp[i - 1] + a[i] ;
		if(i - c >= 0)
			y = dp[i - c] + sum[i] - sum[i - c] - query(i - c + 1 , i) ;
		dp[i] = min(dp[i] , y) ;
	}
	printf("%lld\n" , dp[n]) ;
	return 0 ;
}

 

發佈了231 篇原創文章 · 獲贊 12 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章