【单调队列】

单调队列优化

做动态规划时常常会见到形如这样的转移方程:\(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;
}
posted @ 2019-08-21 21:51  委屈的咸鱼鱼鱼鱼  阅读(95)  评论(0编辑  收藏  举报