单调队列优化DP
单调队列
例题 P1886 滑动窗口 /【模板】单调队列
单调队列里面的值都是单调的,而且相当于在一个双端队列上操作。
对于求最小值:当遍历到一个
对于求最大值同理。
人生又不是独木桥,每个人有自己的步调。
#include <bits/stdc++.h> #define N 1000006 using namespace std; int n,k,a[N],q[N]; int main(){ scanf("%d%d",&n,&k); for(int i = 1;i <= n;i ++) scanf("%d",&a[i]); int head = 1,tail = 0; for(int i = 1;i <= n;i ++){ while(tail >= head and a[q[tail]] >= a[i]) tail --; q[++tail] = i; while(q[head] <= i - k) head ++; if(i >= k) printf("%d ",a[q[head]]); }puts(""); head = 1,tail = 0; for(int i = 1;i <= n;i ++){ while(tail >= head and a[q[tail]] <= a[i]) tail --; q[++tail] = i; while(q[head] <= i - k) head ++; if(i >= k) printf("%d ",a[q[head]]); } return 0; }
习题
1. P2698 [USACO12MAR] Flowerpot S
以
不是偷懒,这叫做……灵活变通地努力。
#include <bits/stdc++.h> #define N 100005 using namespace std; int n,D,q1[N],q2[N],num = 2e9; struct A{int x,y;}a[N]; bool cmp(A a,A b){return a.x < b.x;} int main(){ scanf("%d%d",&n,&D); for(int i = 1;i <= n;i ++) scanf("%d%d",&a[i].x,&a[i].y); sort(a + 1,a + n + 1,cmp); int head1 = 1,head2 = 1,tail1 = 0,tail2 = 0,j = 1; for(int i = 1;i <= n;i ++){ if(i != 1){ while(a[q1[head1]].x < a[i].x) head1 ++; while(a[q2[head2]].x < a[i].x) head2 ++; } for(;j <= n;j ++){ if(a[q1[head1]].y - a[q2[head2]].y >= D){num = min(num,abs(a[q1[head1]].x - a[q2[head2]].x));break;} while(tail1 >= head1 and a[j].y >= a[q1[tail1]].y) tail1 --;q1[++tail1] = j; while(tail2 >= head2 and a[j].y <= a[q2[tail2]].y) tail2 --;q2[++tail2] = j; } if(a[q1[head1]].y - a[q2[head2]].y >= D) num = min(num,abs(a[q1[head1]].x - a[q2[head2]].x)); if(j == n + 1) break; } if(num == 2e9) puts("-1"); else printf("%d",num); return 0; }
单调队列优化DP
例题 CF372C Watching Fireworks is Fun
得到状态转移方程
使用单调队列维护并使用滚动数组优化空间。
做自己内心的君主,胜过众望所归的英雄。
#include <bits/stdc++.h> #define N 150004 #define int long long using namespace std; int n,m,d,a[N],b[N],t[N],ans = -1e18,f[2][N],q[N],fl = 1; signed main(){ scanf("%lld%lld%lld",&n,&m,&d); for(int i = 1;i <= m;i ++) scanf("%lld%lld%lld",&a[i],&b[i],&t[i]); for(int i = 1;i <= n;i ++) f[1][i] = -1e18; for(int i = 1;i <= m;i ++){ int head = 1,tail = 0,k = 1; for(int j = 1;j <= n;j ++){ for(;k <= min(n,j + d * (t[i] - t[i - 1]));k ++){ while(tail >= head and f[fl ^ 1][q[tail]] <= f[fl ^ 1][k]) tail --; q[++tail] = k; } while(tail >= head and q[head] < max(1ll,j - d * (t[i] - t[i - 1]))) head ++; f[fl][j] = f[fl ^ 1][q[head]] - abs(a[i] - j) + b[i]; } fl ^= 1; } for(int i = 1;i <= n;i ++) ans = max(ans,f[fl ^ 1][i]); printf("%lld",ans); return 0; }
习题
1. Loj10176 最大连续和
朴素做法:
然后我们可以转化为
别羡慕,我可是凭本事摸鱼的。
#include <bits/stdc++.h> #define N 200005 using namespace std; int n,m,a[N],num[N],ans = -1e9,q[N]; int main(){ scanf("%d%d",&n,&m); for(int i = 1;i <= n;i ++) scanf("%d",&a[i]),num[i] = num[i - 1] + a[i]; int head = 1,tail = 1;q[1] = 0; for(int i = 1;i <= n;i ++){ ans = max(ans,num[i] - num[q[head]]); while(tail >= head and num[q[tail]] >= num[i]) tail --; q[++tail] = i;while(q[head] <= i - m) head ++; } printf("%d",ans); return 0; }
2. P2254 [NOI2005] 瑰丽华尔兹
考虑优化。
再次考虑优化。我们知道,当前位置一定与上一个合法位置同一行或者同一列,这取决于这次移动的方向。
我们以当前时间段向南为例,设当前时间段的长度为
对自己严苛的人,怎么可能对他人宽容。
#include <bits/stdc++.h> #define N 205 using namespace std; int f[2][N][N],q[N],n,m,X,Y,fl,ans;char a[N][N]; void work1(int len){ for(int j = 1;j <= m;j ++){ int head = 1,tail = 0,k = 1; for(int i = 1;i <= n;i ++){ if(a[i][j] == 'x') continue; k = max(k,i); for(;k <= min(i + len,n);k ++){ if(a[k][j] == 'x') break; while(tail >= head and f[fl ^ 1][k][j] + k >= f[fl ^ 1][q[tail]][j] + q[tail]) tail --; q[++tail] = k; } while(tail >= head and q[head] < i) head ++; f[fl][i][j] = f[fl ^ 1][q[head]][j] + q[head] - i; } } fl ^= 1; } void work2(int len){ for(int j = 1;j <= m;j ++){ int head = 1,tail = 0,k = n; for(int i = n;i >= 1;i --){ if(a[i][j] == 'x') continue; k = min(k,i); for(;k >= max(i - len,1);k --){ if(a[k][j] == 'x') break; while(tail >= head and f[fl ^ 1][k][j] - k >= f[fl ^ 1][q[tail]][j] - q[tail]) tail --; q[++tail] = k; } while(tail >= head and q[head] > i) head ++; f[fl][i][j] = f[fl ^ 1][q[head]][j] - q[head] + i; } } fl ^= 1; } void work3(int len){ for(int i = 1;i <= n;i ++){ int head = 1,tail = 0,k = 1; for(int j = 1;j <= m;j ++){ if(a[i][j] == 'x') continue; k = max(k,j); for(;k <= min(j + len,m);k ++){ if(a[i][k] == 'x') break; while(tail >= head and f[fl ^ 1][i][k] + k >= f[fl ^ 1][i][q[tail]] + q[tail]) tail --; q[++tail] = k; } while(tail >= head and q[head] < j) head ++; f[fl][i][j] = f[fl ^ 1][i][q[head]] + q[head] - j; } } fl ^= 1; } void work4(int len){ for(int i = 1;i <= n;i ++){ int head = 1,tail = 0,k = m; for(int j = m;j >= 1;j --){ if(a[i][j] == 'x') continue; k = min(k,j); for(;k >= max(j - len,1);k --){ if(a[i][k] == 'x') break; while(tail >= head and f[fl ^ 1][i][k] - k >= f[fl ^ 1][i][q[tail]] - q[tail]) tail --; q[++tail] = k; } while(tail >= head and q[head] > j) head ++; f[fl][i][j] = f[fl ^ 1][i][q[head]] - q[head] + j; } } fl ^= 1; } int main(){ int k;scanf("%d%d%d%d%d",&n,&m,&X,&Y,&k); for(int i = 1;i <= n;i ++) for(int j = 1;j <= m;j ++) cin >> a[i][j],f[0][i][j] = -1e7; f[0][X][Y] = 0,fl = 1; for(int i = 1,s,t,d;i <= k;i ++){ scanf("%d%d%d",&s,&t,&d); if(d == 1) work1(t - s + 1); if(d == 2) work2(t - s + 1); if(d == 3) work3(t - s + 1); if(d == 4) work4(t - s + 1); } for(int i = 1;i <= n;i ++) for(int j = 1;j <= m;j ++) ans = max(f[fl ^ 1][i][j],ans); printf("%d",ans); return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步