動態規劃的單調佇列優化(含多重揹包)
什麼是單調佇列
單調佇列就是元素單調的佇列,譬如一個佇列中的元素為1,2,3,4,5,6,單調遞增,這就是一個單調佇列。咱們先看一道單調佇列的模板題:poj2823/洛谷P1886
怎麼維護單調佇列呢?譬如維護一個單調遞增的佇列,就是要進入一個元素的時候,把隊尾小於它的元素統統出隊即可。而在例題中,我們還要記錄每個元素在原來陣列中的下標以確定是否可用,如果已經出了當前視窗,則出隊。
程式碼:
void getmin(){//單調遞增
int he=1,ta=1,i;
for(i=1;i<=n;i++){
while(he<ta&&q[ta-1 ]>=a[i])ta--;
q[ta]=a[i];bj[ta]=i;ta++;
if(i>=m){
while(he<ta&&bj[he]<=i-m)he++;
printf("%d ",q[he]);
}
}
}
void getmax(){//單調遞減
int he=1,ta=1,i;
for(i=1;i<=n;i++){
while(he<ta&&q[ta-1]<=a[i])ta--;
q[ta]=a[i];bj[ta]=i;ta++;
if (i>=m){
while(he<ta&&bj[he]<=i-m)he++;
printf("%d ",q[he]);
}
}
}
單調佇列優化動態規劃
例題1:洛谷P1725 琪露諾
連結:走你╭(′▽`)╯
這題就當是單調佇列入門啦。
大家都知道
直接這麼dp肯定超時,那麼我們可以把
單調佇列一定不能刪除還有可能用到的元素,也不能新增暫時不會用的元素,所以我們要確保在用單調佇列時,
所以就有了程式碼中的寫法
程式碼:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
using namespace std;
#define ll long long
ll read(){
ll q=0,w=1;char ch=' ';
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')q=q*10+(ll)(ch-'0'),ch=getchar();
return q*w;
}
const int maxn=200005;
int n,l,r;
ll v[maxn],f[maxn],ans;
int bj[maxn];
int main()
{
int i,j,ta=1,he=1;ll kl;
n=read();l=read();r=read();
for(i=0;i<=n;i++)v[i]=read();
f[0]=v[0];
for(i=l;i<=n;i++){
while(he<ta&&f[bj[ta-1]]<=f[i-l])ta--;
bj[ta]=i-l;ta++;
while(he<ta&&bj[he]<i-r)he++;
f[i]=f[bj[he]]+v[i];
if(i>=n-r)ans=max(ans,f[i]);
}
printf("%lld",ans);
return 0;
}
例題2:UESSTC594我要長高
連結:走你╭(′▽`)╯
這題充滿了惡意啊……
容易想到用
現在我們分類討論一下,假如
變形可得:
顯然前面那一坨可以塞在一個單調佇列裡來求小於j的情況下的最優k,具體怎麼實現看程式碼吧。然後
得到了美妙的程式碼:
#include<iostream>
#include<cstdio>
#include<climits>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int read(){
int q=0;char ch=' ';
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')q=q*10+ch-'0',ch=getchar();
return q;
}
const int maxn=105;
int n,c,inf=0x3f3f3f3f;
int f[2][maxn],q[maxn];
int main()
{
int x,i,j,t,ans,he,ta;
while(scanf("%d%d",&n,&c)==2){
x=read();t=1;
for(i=0;i<x;i++)f[t][i]=inf;
for(i=x;i<=100;i++)f[t][i]=(x-i)*(x-i);
for(i=2;i<=n;i++){
t=i&1;x=read();
he=ta=1;
for(j=0;j<=100;j++){//比前一個人高,顯然弄到j的時候k取0~j-1的情況都已討論過
int kl=f[t^1][j]-j*c;
while(he<ta&&q[ta-1]>=kl)ta--;
q[ta]=kl;ta++;
if(j<x)f[t][j]=inf;
else f[t][j]=q[he]+j*c+(x-j)*(x-j);
}
he=ta=1;
for(j=100;j>=0;j--){//比前一個人矮,顯然弄到j的時候k取j+1~100的情況都已討論過
int kl=f[t^1][j]+j*c;
while(he<ta&&q[ta-1]>=kl)ta--;
q[ta]=kl;ta++;
if(j<x)f[t][j]=inf;
else f[t][j]=min(f[t][j],q[he]-j*c+(x-j)*(x-j));
}
}
t=n&1;ans=inf;
for(i=0;i<=100;i++)ans=min(ans,f[t][i]);
printf("%d\n",ans);
}
return 0;
}
例題3:HDU3401
連結:走你╭(′▽`)╯
題目大意是買股票,第i天買花
如果不交易,那麼
如果買
如果賣
(因為不交易的狀態已經轉移了,所以買和賣只要考慮第
然後我們把狀態轉移方程變形一下,就是
買:
賣: