【ybtoj】【單調佇列】燃放煙火
阿新 • • 發佈:2021-09-14
題意
前言
學習一個新的演算法,總是要仔細考慮一些深入的問題
題解
首先清楚單調佇列是一個主要用來優化 dp 的演算法,所以先想出 dp 轉移式子,再通過單調佇列優化掉一維的複雜度才是正常的開啟方式
迴歸本題,很容易想到設計\(dp(i,j)\)表示前\(i\)個煙花的時候在第\(j\)個位置能獲得的最大幸福值
轉移也比較顯然,再列舉一維\(k\)表示第\(i-1\)個煙火的時候在什麼位置,就有\(dp(i,j)=max \{dp(i-1,k)\} +b_i-abs(a_i-j);\)
因為是一個區間單調地向右移動同時維護最大值,所以\(k\)這一維就可以單調佇列優化了
需要注意的是,\(j\)
由於有負值,dp 陣列需要稍微特殊處理一下
程式碼
#include<bits/stdc++.h> using namespace std; #define ll long long const int INF = 0x3f3f3f3f,N = 1.5e5+10,M = 305; inline ll read() { ll ret=0;char ch=' ',c=getchar(); while(!(c>='0'&&c<='9')) ch=c,c=getchar(); while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar(); return ch=='-'?-ret:ret; } struct node { int a,b,t; inline bool operator < (const node& oth) const {return t<oth.t;} }f[M]; int dp[M][N]; int n,m,d,q[N]; int main() { n=read(),m=read(),d=read(); for(int i=1;i<=m;i++) f[i].a=read(),f[i].b=read(),f[i].t=read(); //sort(f+1,f+m+1); 輸入保證t不下降 //memset(dp,-0x3f,sizeof(dp)); //for(int i=1;i<=n;i++) dp[m][i]=-INF; for(int i=1;i<=m;i++) { int l=1,r=0; for(int j=1;j<=n;j++) { while(r>=l&&j-q[l]>(f[i].t-f[i-1].t)*d) l++; while(r>=l&&dp[i-1][q[r]]<=dp[i-1][j]) r--; q[++r]=j; if(r>=l) dp[i][j]=dp[i-1][q[l]]+f[i].b-abs(f[i].a-j); //printf("dp:%d update\n",dp[i-1][q[l]]+f[i].b-abs(f[i].a-j)); } l=1,r=0; for(int j=n;j>=1;j--) { while(r>=l&&q[l]-j>(f[i].t-f[i-1].t)*d) l++; while(r>=l&&dp[i-1][q[r]]<=dp[i-1][j]) r--; q[++r]=j; if(r>=l) dp[i][j]=max(dp[i][j],dp[i-1][q[l]]+f[i].b-abs(f[i].a-j)); //printf("dp:%d update\n",dp[i-1][q[l]]+f[i].b-abs(f[i].a-j)); } //printf("#%d:(%d,%d)\n",i,l,r); } int res=-INF; for(int i=1;i<=n;i++) res=max(res,dp[m][i]); printf("%d",res); return 0; }