筆者大概看了一下單調佇列對於dp的優化,故撰此文,望有幫助。
(dp還是推式子難啊qwq)
例題1.
題目大意:在n個數的序列中,選擇數字,使得其連續不超過k個數,且和最大。
本題的方程相對好推:設dp[i][0/1]為到了第i個數,且第i個數不選/選的最大值。
則有轉移:dp[i][0]=max(dp[i-1][0],dp[i-1][1])
dp[i][1]=max,i-k<=j
列舉j即可。
但是題目會這麼讓你水過嗎?
發現會超時,優化不可避免。
仔細觀察方程,考慮它的特殊性。
對於方程1,我們不便再多做什麼。但是對於方程2,顯然仍有優化餘地。
我們將sum[i]提出來,得到:
dp[i][1]=max+sum[i].
這個方程只與j有關,我們讓max裡面的最大就好了。
維護它,我們可以用堆,也可以用單調佇列,線段樹。
本文主要講對於單調佇列的優化。它可以保證o(n)的時間複雜度。
首先,我們明確一點,我們維護的隊首元素最大。顯然,佇列中的數字要單調遞減。
其次,我們要嚴格確保我們查詢的區間,保證佇列中沒有沒用的數字。
並且每次列舉到下乙個數的時候,注意更新佇列。
考慮何時更新更優:
首先,當這個數字不在需要的範圍的時候,刪除即可。
其次,對於新插入的數字,我們要從隊尾插入,比較哪個值更優:
判斷它們的「浪費情況」即可。
用q表示佇列,s表示字首和,則判斷:
s[q[head]]-f[q[head]][0]>s[i]-f[i][0]&&head<=tail
如果符合的話,就把它刪掉吧。因為隊尾的元素所浪費的比新插入的值多,顯然一定不如它優。
由此,我們已經保證了單調佇列的穩定複雜度。
給出**:
#include#includeusing
namespace
std;
long
long q[2000000],f[2000000][2
];long
long n,k,s[2000000],a[2000000
];long
long
tail,head;
intmain()
tail=head=1;//
初始化
for(int i=1;i<=n;++i)printf("
%lld\n
",max(f[n][0],f[n][1
]));
return0;
}
雙倍經驗:p2034
持續更新中。
單調佇列 優化DP
佇列元素保持單調遞增 減 而保持的方式就是通過插隊,把隊尾破壞了單調性的數全部擠掉,從而使佇列元素保持單調。單調佇列的作用 優化dp。許多單調佇列優化的dp可以使複雜度直接降維,下面就以最簡單的一道題為例 在某兩座城市之間有 n 個烽火台,每個烽火台發出訊號都有一定代價。為了使情報準確地傳遞,在連續...
單調佇列優化dp
形如f i max wi的問題都可以用單調佇列優化。例題 板題 注意乙個地方 求完所有的f後 ans不是f n 而是後面的一段字尾的f 注意字尾的左端點。很顯然是rmq問題 計算字首和sum i 對於固定的右端點 i,我們想讓答案最大等價於max,可以用個單調佇列維護。但是隨便乙個資料結構直接on ...
DP 單調佇列優化
使用單調佇列優化的題目具有這樣的特點,他需要我們維持一段區間內的某個最優值,這個區間是隨著遍歷的順序變化的,但是其變化一定具有這樣的特性,也即維持的區間左右端點一定是單調遞增的,而不能出現回流的現象,否則我們在維持佇列單調性過程中剪枝的資料可能是新的區間中的最大值。維持區間最優值的方法有很多,例如靜...