題目連結:http://www.lydsy.com/JudgeOnline/problem.php?id=2442
題意:
有n個數a[i]從左到右排成一排。
你可以任意選數,但是連續的數不能超過k個。
問你最大的選數之和。
題解:
表示狀態:
dp[i]表示考慮了第i個數的最大之和。
找出答案:
ans = dp[n]
將所有的數都考慮過了
如何轉移:
對於a[i],要麼選,要麼不選。
(1)如果不選,則dp[i] = max dp[i-1]。
(2)如果選,則最多往前選k個數,且在i-k的位置一定不能選。
所以:
dp[i] = max dp[j] + sum(j+2,i) (i-k-1 <= j <= i-2)
變成字首和的形式:
dp[i] = max dp[j] + sum[i] - sum[j+1]
也就是:
dp[i] = max(dp[j] - sum[j+1]) + sum[i]
對於dp[j] - sum[j+1]這一部分,可以用單調佇列優化。
邊界條件:
dp[0] = 0
q[head++] = Node(-1,0)
-1為假想的位置,只是為了在n == 1的時候能夠用到0這個值。
AC Code:
1 // state expression: 2 // dp[i] = max efficiency 3 // i: selected ith cow 4 // 5 // find the answer: 6 // max dp[n] 7 // 8 // transferring: 9 // dp[i] = max(dp[j] + sum(j+2,i), dp[i-1]) 10 // dp[i] = max(dp[j] + sum[i] - sum[j+1], dp[i-1]) 11 // dp[i] = max(dp[j] - sum[j+1] + sum[i], dp[i-1]) 12 // i-k-1 <= j <= i-2 13 // 14 // boundary: 15 // dp[0] = 0 16 #include <iostream> 17 #include <stdio.h> 18 #include <string.h> 19 #define MAX_N 100005 20 21 using namespace std; 22 23 struct Node 24 { 25 int idx; 26 long long val; 27 Node(int _idx,long long _val) 28 { 29 idx=_idx; 30 val=_val; 31 } 32 Node(){} 33 }; 34 35 int n,k; 36 int head=0; 37 int tail=0; 38 int e[MAX_N]; 39 long long dp[MAX_N]; 40 long long sum[MAX_N]; 41 Node q[MAX_N]; 42 43 void read() 44 { 45 cin>>n>>k; 46 sum[0]=0; 47 for(int i=1;i<=n;i++) 48 { 49 cin>>e[i]; 50 sum[i]=sum[i-1]+e[i]; 51 } 52 } 53 54 void solve() 55 { 56 dp[0]=0; 57 q[tail++]=Node(-1,0); 58 for(int i=1;i<=n;i++) 59 { 60 if(i>=2) 61 { 62 while(head<tail && q[tail-1].val<dp[i-2]-sum[i-1]) tail--; 63 q[tail++]=Node(i-2,dp[i-2]-sum[i-1]); 64 } 65 while(head<tail && q[head].idx<i-k-1) head++; 66 dp[i]=max(q[head].val+sum[i],dp[i-1]); 67 } 68 } 69 70 void print() 71 { 72 cout<<dp[n]<<endl; 73 } 74 75 int main() 76 { 77 read(); 78 solve(); 79 print(); 80 }