POJ1821 Fence
傳送門
這道題是一道很好的單調隊列優(yōu)化DP的例子。
題目大意是有n個工人,每個人可以粉刷一段長度不超過l[i]的墻,如果一個人粉刷了那么他必須要粉刷第s[i]塊墻,一個人粉刷一塊墻能得到p[i]的錢,求所有工人得到的錢的最大值。
我們首先把所有工人按s[i]排序,這樣方便我們線性DP。
考慮DP,用dp[i][j]表示前i個工人刷j塊的得到的錢的最大值,那么我們分類討論,首先是一個人不刷和一塊墻不刷的情況,那么就有dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
之后,我們考慮這個工人刷墻的情況。因為這個人必須要刷s[i],那這個人的轉移必然是從s[i] - l[i] ~ s[i]這一段轉移過來的。否則的話就成了不合法情況。那么就有dp[i][j] = max{dp[i-1][k] + (j - k) * p[i]} (s[i] - l[i] <= k <= s[i])
這個式子我們樸素的做法是先枚舉i,對于每一個工人i,枚舉它粉刷的塊數j,之后再枚舉轉移的范圍。不過這樣會超時,我們考慮優(yōu)化。我們發(fā)現如果把j,k分離出來,那么對于每一個j(狀態(tài)),它對應的j * p[i]是一個定值,只有內部的式子隨著k(決策)的變化而改變,而且其實對于每一個j,有很大一部分的可選取的決策區(qū)間是重復的。
說到這里,我們就想到用單調隊列維護啦!!那么我們的想法就是,對于每一個工人,我們首先把s[i] - l[i] ~ s[i]這一段區(qū)間之內的所有決策壓入單調隊列,之后隨著j的增加,所能選取的k的區(qū)間左端點隨之變大。也就是說實際上,我們只要維護一段左端點變大,右端點不動的區(qū)間最大值就可以了。
所以一般來說,單調隊列優(yōu)化DP的套路就是,如果一個DP方程能被拆成每一個狀態(tài)都是從一個決策區(qū)間中選取最優(yōu)的轉移,然后決策區(qū)間隨著狀態(tài)變化左右端點變化,而且有大量重復,我們就可以先拆分式子,拆成內層只與決策有關,這樣直接用單調隊列維護決策的極值,就省去了一層循環(huán),優(yōu)化了時間。
看一下代碼。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<set> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n')using namespace std; typedef long long ll; const int M = 10005; const int INF = 1000000009;int read() {int ans = 0,op = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') op = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){ans *= 10;ans += ch - '0';ch = getchar();}return ans * op; }struct worker {int l,p,s;bool operator < (const worker &g) const{return s < g.s;} }a[105];int n,k,dp[105][20005],q[20005],head,tail;int calc(int i,int x) {return dp[i-1][x] - x * a[i].p; }int main() {n = read(),k = read();rep(i,1,k) a[i].l = read(),a[i].p = read(),a[i].s = read();sort(a+1,a+1+k);rep(i,1,k){head = 1,tail = 0;rep(j,max(0,a[i].s-a[i].l),a[i].s-1){while(head <= tail && calc(i,q[tail]) < calc(i,j)) tail--;q[++tail] = j;}rep(j,1,n){dp[i][j] = max(dp[i-1][j],dp[i][j-1]);if(j >= a[i].s){while(head <= tail && q[head] < j - a[i].l) head++;if(head <= tail) dp[i][j] = max(dp[i][j],calc(i,q[head]) + j * a[i].p);}}}printf("%d\n",dp[k][n]);return 0; }?
轉載于:https://www.cnblogs.com/captain1/p/9929440.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的POJ1821 Fence的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 龙之国物语麦芽镇社交之路
- 下一篇: 米游社可以用什么登录