【CF1077F2】Pictures with Kittens 單調佇列+dp
阿新 • • 發佈:2018-12-06
題目大意:給定一個長度為 N 的序列,點有點權,從序列中選出恰好 X 個數,並且保證任意連續的 K 個數中均有一個被選中,求選出的點權最大是多少。
題解:此題可以作為 烽火傳遞+ 來處理,只不過在烽火傳遞的基礎上加了選出恰好 X 個數,因此只需在狀態維度上加上一維選出的個數即可,\(dp[i][j]\) 表示前 i 個數中選出 j 個數,且第 i 個數被選中的最優解,因此有狀態轉移方程:\(dp[i][j]=max\{dp[k][j-1],k\in[i-m,i-1] \}+val[i]\),直接用單調佇列進行優化即可。
同時,可以直接在初始化的時候將所有值設為無窮,避免了無效的狀態轉移,從而避免了不合法的解對答案的貢獻,也就避免了討論何時輸出 -1 的問題。
程式碼如下
#include<bits/stdc++.h> using namespace std; const int maxn=5010; int n,m,tot,val[maxn],q[maxn<<1],l,r; long long dp[maxn][maxn],ans; void read_and_parse(){ scanf("%d%d%d",&n,&m,&tot); for(int i=1;i<=n;i++)scanf("%d",&val[i]); } void solve(){ memset(dp,0xcf,sizeof(dp)); dp[0][0]=0; for(int i=1;i<=tot;i++){ l=1,r=0; for(int j=1;j<=n;j++){ while(l<=r&&q[l]<j-m)++l; while(l<=r&&dp[j-1][i-1]>dp[q[r]][i-1])--r; q[++r]=j-1; dp[j][i]=dp[q[l]][i-1]+val[j]; } } ans=-1; for(int i=n-m+1;i<=n;i++)ans=max(ans,dp[i][tot]); printf("%lld\n",ans); } int main(){ read_and_parse(); solve(); return 0; }