單調隊列總結

單調隊列總結


前言

單調隊列易於理解,這裏很少說實現了,只說一些例題和用途html

單調隊列實質是O(n)求一段序列中多段有相同長度限制的子序列的最值c++


裸體裸題:

1.滑動窗口:板子數組

2.理想正方形 :(其實也是板子)每一橫行用維護單調隊列維護,稱爲q1,再用另外一組單調隊列維護一列,稱爲q2,q2內元素爲q1隊首
優化

3.修築綠化帶:與上一題目差異不大,先預處理出以x,y爲右下角的C*D矩陣的和,記做d[ i ][ j ],而後在枚舉A*B矩陣右下角,在(A-2)*(B-2)內部單調隊列取d[ i ][ j ]最值,邊界有點噁心spa


優化DP:

股票交易code

題目概述:你有無限的本金,你要炒股(我也不知道爲啥htm

天天股票買入爲 APi,賣出價爲BPi,天天最多買ASi股,最多賣BSi股,每次操做後(買入賣出均算操做)需隔w天進行下次操做(會累的嘛),你手裏股票不能超過MAXP股,求T天后的最大收益。
blog

考慮暴力DP:隊列

f[ i ][ j ]爲到第i天手裏有j股的最大收益;get

1.憑空買入 f[ i ][ j ]= -APi * j (j <= ASi );

2.摸魚(啥也不幹)f[ i ][ j ] = max(f[ i ][ j ] ,f[ i-1 ][ j ]);

3.從之前買/賣 考慮到上一步,咱們已經把第i天以前的最優解轉移到第i天了,因此咱們直接取f[ i-w-1 ][ j ]進行操做必定是最優的

看到這裏暴力DP您必定會寫了QAQ

考慮單調隊列優化:

咱們發現第三步的轉移方程是O(n^2)級別的,外面再套個天數就是N^3了,TLE;

列出第三步的買入方程:

f[ i ][ j ] = max{f[ i ][ j ],f[ i-w-1 ][ j-k ] - k*APi}(0<k<=ASi);

略做轉化,將(j-k)設爲 k,則方程變爲

f[ i ][ j ] = max {f[ i-w-1 ][ k ] + k*APi - j*APi}(j-ASi<=k< j);

將 j*APi 提出

f[ i ][ j ] = max {f[ i-w-1 ][ k ] + k*APi }- j*APi(j-ASi<=k< j);

咱們發現問題變爲了求 [ j-ASi,j)這段區間內 f[ i-w-1 ][ k ] + k*APi 的最大值,能夠用單調隊列優化

買入操做相似,不推了(懶

#include<bits/stdc++.h>
using namespace std; inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,m,w,ans; struct point { int ap,bp,as,bs; }g[2010]; int f[2010][2010]; int q[2010][2]; int head,tail; signed main() { n=read(),m=read(),w=read(); memset(f,-0x3f,sizeof(f)); for(int i=1;i<=n;++i) { g[i].ap=read(),g[i].bp=read(),g[i].as=read(),g[i].bs=read(); f[i][0]=0; } for(int i=1;i<=n;++i) { for(int j=1;j<=g[i].as;++j) f[i][j]=-j*g[i].ap; for(int j=0;j<=m;++j) f[i][j]=max(f[i][j],f[i-1][j]); head=0,tail=-1; if(i<=w) continue; for(int j=0;j<=m;++j) { while(q[head][0]<j-g[i].as&&head<=tail) ++head; while(f[i-w-1][j]+j*g[i].ap>=q[tail][1]&&head<=tail) --tail; q[++tail][0]=j; q[tail][1]=f[i-w-1][j]+j*g[i].ap; if(head<=tail) f[i][j]=max(f[i][j],q[head][1]-j*g[i].ap); } head=0,tail=-1; for(int j=m;j>=0;--j) { while(q[head][0]>j+g[i].bs&&head<=tail) ++head; while(f[i-w-1][j]+j*g[i].bp>=q[tail][1]&&head<=tail) --tail; q[++tail][0]=j; q[tail][1]=f[i-w-1][j]+j*g[i].bp; if(head<=tail) f[i][j]=max(f[i][j],q[head][1]-j*g[i].bp); } for(int j=0;j<=m;++j) ans=max(ans,f[i][j]); } printf("%d\n",ans); return 0; }

 

 瑰麗華爾茲

題目概述:在n*m網格上有我的,初始在(x,y)點,K次輸入,每次給出st,ed,和d,他在st到ed時間段內每秒能夠朝着d方向走一格或不走,地圖上有一些障礙,人不能碰到障礙或走出地圖,請問這我的最後最多走多少格;

暴力DP:

設f[ t ][ i ][ j ]爲在第t次給出後在(i,j)處最多走了多少格

每次給出st,ed,和 d 後循環每一個節點,直接在每一個節點模擬

複雜度O(K*N*M*T);

加個滾動數組優化你A了(?)

本着嚴謹的態度咱們仍是談一下單調隊列優化:

令ed=ed-st+1;

繼續枚舉(i,j),問題爲在(i,j)處從某一距離不超過ed方向轉移過來的最大值,符合單調隊列本質

#include<bits/stdc++.h>
using namespace std; inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,m,x,y,k,ans; int a[210][210]; char ch; int f[2][210][210]; int t,tx,ty,st,ed,d; int dx[]={0,1,-1,0,0}; int dy[]={0,0,0,1,-1}; int q[210][2]; int head,tail; inline void work1(int y) { for(int x=n+1;x>=1;--x) { if(!a[x][y]) { head=0,tail=-1; continue; } while(q[head][0]-x>ed&&head<=tail) ++head; while(f[t^1][x][y]+x>=q[tail][1]&&head<=tail) --tail; q[++tail][0]=x; q[tail][1]=f[t^1][x][y]+x; f[t][x][y]=max(f[t][x][y],q[head][1]-x); } } inline void work2(int y) { for(int x=0;x<=n;++x) { if(!a[x][y]) { head=0,tail=-1; continue; } while(x-q[head][0]>ed&&head<=tail) ++head; while(f[t^1][x][y]-x>=q[tail][1]&&head<=tail) --tail; q[++tail][0]=x; q[tail][1]=f[t^1][x][y]-x; f[t][x][y]=max(f[t][x][y],q[head][1]+x); } } inline void work3(int x) { for(int y=m+1;y>=1;--y) { if(!a[x][y]) { head=0,tail=-1; continue; } while(q[head][0]-y>ed&&head<=tail) ++head; while(f[t^1][x][y]+y>=q[tail][1]&&head<=tail) --tail; q[++tail][0]=y; q[tail][1]=f[t^1][x][y]+y; f[t][x][y]=max(f[t][x][y],q[head][1]-y); } } inline void work4(int x) { for(int y=0;y<=m;++y) { if(!a[x][y]) { head=0,tail=-1; continue; } while(y-q[head][0]>ed&&head<=tail) ++head; while(f[t^1][x][y]-y>=q[tail][1]&&head<=tail) --tail; q[++tail][0]=y; q[tail][1]=f[t^1][x][y]-y; f[t][x][y]=max(f[t][x][y],q[head][1]+y); } } signed main() { n=read(),m=read(),x=read(),y=read(),k=read(); for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { for(ch=getchar();(ch!='.'&&ch!='x');ch=getchar()); a[i][j]=(ch=='.'); } } memset(f,-0x3f,sizeof(f)); f[t][x][y]=0; for(int p=1;p<=k;++p) { st=read(),ed=read(),d=read(); ed=ed-st+1; t^=1; if(d==1)//
        for(int j=1;j<=m;++j) work1(j); if(d==2)//
        for(int j=1;j<=m;++j) work2(j); if(d==3)//西
        for(int i=1;i<=n;++i) work3(i); if(d==4) for(int i=1;i<=n;++i) work4(i); } for(int x=1;x<=n;++x) for(int y=1;y<=m;++y) ans=max(ans,f[t][x][y]); printf("%d\n",ans); return 0; }
相關文章
相關標籤/搜索