【单调队列】
单调队列优化
做动态规划时常常会见到形如这样的转移方程:\(f[i] = optimize\{g(j)|L[i] ≤ j < i \rbrace+w[i]\)
其中\(L[1] ≤ L[2] ≤ · · · ≤ L[n]\) (\(g[j]\)表示一个和j或\(f[j]\)有关的函数,\(w[i]\)表示一个和i有关的函数)
有这样一个性质:如果存在两个数 \(j,k\),使得 \(k ≤ j\),而且\(g(k) ≤ g(j) (opt = max)\) 或 \(g(j) ≤ g(k)(opt = min)\),则决策k 是毫无用处的。
根据 \(L[i]\) 单调的特性,如果 k 可以作为合法决策,那么 j一定可以作为合法决策,又因为 j 比 k 要优 (注意: 在这个经典模型中,“优”是绝对的,与当前正在计算的状态⽆关),因此如果把表中的决策按照 j 排序的话,则 \(g(j)\) 必然不升\((opt=max)\) 或必然不降 \((opt=min)\)。
1、 计算\(g(x)\),并将其插入到单调队列的尾部,同时维持队列的单调性(不断地出队,直到队列单调为止)。
2、 队首元素出队,直到队首元素在给定的范围中。
3、 此时,队首元素就是状态f(x)的最优决策,
重复上述步骤直到所有的函数值均被计算出来。不难看出这样的算法均摊时间复杂度是\(O(1)\)的 因此使用单调队列即可将原本 \(O(N^2)\) 的复杂度降至\(O(N)\)
实战
滑动窗口
#include<bits/stdc++.h>
using namespace std;
const int N=1000000+5,M=2e5+5,inf=0x3f3f3f3f,P=19650827;
int n,m,a[N],q1[N],q2[N],ans1[N],ans2[N];
template <class t>void rd(t &x){
x=0;int w=0;char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=w?-x:x;
}
int main(){
rd(n),rd(m);
for(int i=1;i<=n;++i) rd(a[i]);
int l1,r1=0,l2=l1=1,r2=0;
for(int i=1;i<=n;++i){
while(l1<=r1&&a[i]<a[q1[r1]]) --r1;
while(l2<=r2&&a[i]>a[q2[r2]]) --r2;
while(l1<=r1&&q1[l1]<=i-m) ++l1;
while(l2<=r2&&q2[l2]<=i-m) ++l2;
q1[++r1]=i,q2[++r2]=i;
if(l1<=r1) ans1[i]=a[q1[l1]];
if(l2<=r2) ans2[i]=a[q2[l2]];
}
for(int i=m;i<=n;++i) printf("%d ",ans1[i]);
puts("");
for(int i=m;i<=n;++i) printf("%d ",ans2[i]);
return 0;
}
P3957跳房子
二分+单调队列
g越大 灵活性越高 ....所以二分
在g不变的情况下 随着向右跳 l和r也在递增 所以用单调队列
(有问题)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define Max(x,y) ((x)>(y)?(x):(y))
const int N=500000+5,M=2e5+5,inf=0x3f3f3f3f,P=19650827;
int n,d,k,a[N],s[N];
ll sum=0,f[N];
template <class t>void rd(t &x){
x=0;int w=0;char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=w?-x:x;
}
int q[N];
bool check(int mid){
for(int i=1;i<=n;++i) f[i]=-inf;
memset(q,0,sizeof(q));
int h=1,t=0,ma=Max(1,d-mid),mb=d+mid;
for(int i=1,j=0;i<=n;++i){
for(;j<i&&s[i]-s[j]>=ma;++j)
if(f[j]!=-inf){//j可到达
while(h<=t&&f[j]>f[q[t]]) --t;
q[++t]=j;
}
while(h<=t&&s[i]-s[q[h]]>mb) ++h;//除去不合法状态
if(h<=t) f[i]=a[i]+f[q[h]];
}
for(int i=1;i<=n;++i) if(f[i]>=k) return 1;
return 0;
}
int main(){
freopen("in2.txt","r",stdin);
//freopen("xor.out","w",stdout);
rd(n),rd(d),rd(k);
int l=0,r=d,mid;
for(int i=1;i<=n;++i) rd(s[i]),rd(a[i]),sum+=Max(0,a[i]),r=Max(r,s[i]);
if(sum<k) return puts("-1"),0;
while(l<r){
mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%d",r);
return 0;
}