单调队列复习

单调性+单调队列+DP优化=(大坑)

单调性

单调性, 在oi中许多算法中,都有用到,最经典的莫属二分 。当然对于函数驻点只有一个的类二次和二次函数,我们还有三分。但是我们今天不讲分治
单调递增,单调递减不知道是啥?其实我觉得,没必要知道这玩意。。当时学的时候,也不没学到函数,(虽然听着一脸懵)。。
所以之所以说单调性。。。是为了。。。为了。。。。(好玩)。。。

单调队列。。(单调的一个函数(还要自己维护))

单调队列,单调队列,结构如其名,数组中的数要维护成单调形式。。
具体的做法。。我们先贴代码。。

#define REP(i,a,b) for(int i(a);i<=(b);++i)
	REP(i,1,n){
		while(c[head]<=i-k&&head<=tail) head++;
		while(a[c[tail]]>a[i]&&tail>=head) tail--;
		c[++tail]=i;if (i>=k) cout<<a[c[head]]<<" ";}
下面再慢慢解析,,,head是头指针,tail是尾指针。。这里的代码是从sliding windows上才下来的。因为和模板没区别链接是---------滑动窗口我们想 单调队列。要维护单调,如果我们直接在数组中存数,后期在删数时就不好了。。所以第一步要知道的是一般存的都是坐标(位置);(删数不知道?下面讲)

既然要维护单调,我们就想,有多少条件在限制呢。。。
想滑动窗口这题

首先,他有一个窗口在数组上滑动,,滑呀滑,可以看到,后面一直有数在进来,前面有数一直在出去。。 如果我们要维护这样一个单调的队列就一定也要有加入和弹出操作

while(c[head]<=i-k&&head<=tail) head++;

可以看到有两个限制语句,而语句的结果就是head++;
想一想,如果head++了;那之前的head里的坐标就pop()出去了。。(手动滑稽)
可以联想一下队列。。。而至于限定语句

我们来慢慢讲

c数组是单调队列,里面存的都是下标,,那么这一句表达的是夏目意思捏

如果c中存的下标已经不满足条件了,我们就应该把它pop()出去

i-k就是现在我们的在数组中滑动的“窗口”的第一个最小开头的坐标

这样就可以保证队列中的数一定在我们要查询的范围内,,
啊,你说如果后面有下标不符合条件的怎么办???
额,我没考虑,

因为不需要考虑。。。。

因为我们的放入,已经把这个问题搞掉了。。。

while(a[c[tail]]>a[i]&&tail>=head) tail--;c[++tail]=i;

可以看到这一句c[++tail]=i ; 这一句保证了前面的下标一定比后面的要小。。
因为REP保证了i是递增的,tail是从尾部加入数,可以理解为筛选i,从单调递增的函数上从前往后取数,一定是单调的(没学过的话(自个yy));

前面还有限制语句,这里最难(个人觉得)当时上的时候(一脸懵逼)

单调(单调)!!!!记住是单调的!
所以我们要维护这样一个单调队列,,如果前面有比他小的(大的)就把他pop()出去
这样才能形成部分单调,,这是从数学的角度

实用的角度
比如我们知道,,单调队列一般都是用于求最值smax,smin啥的。所以我们维护队列的目的最终是为了求smin(在一定范围内)

对于像滑动窗口这种题目,如果真的爆搜,我们会发现。(特不现实)TTTTT飞了

然后我们坐下来分析,,诶后面的好像可以从前面的得出来!,我们可以比较一下最大值(最小值)比我要加的数哪个大好像就可以了!(XXXXXXXXX)
错的一塌糊涂,,,里面有太多要维护的了(因为会删去有用的节点,,(可以自己想想))。。所以一切都集中在这(有用)。。如果我们保证队列中都时有用的呢!!!!
这时候回到代码

while(a[c[tail]]>a[i]&&tail>=head) tail--;c[++tail]=i;

如果现在最后的单调队列最后面下标代表的数比我放的数(大)小,按需求。。我们就把他踢出去,,,
比如举个例子。。我们要求最大值,,我们放进去一个数;然后发现!前面竟然有个比自己大的(更早放进去),但是却比自己弱(比自己小)的吃闲饭的!为甚么呢(你想想后面的数一定比前面的数后被++head)也就是说我放的数存在的时候,前面的数不一定存在。

但前面的数存在的话,我们放进去的数一定一定一定存在(在放这个数字后)!

那么他比我弱,他存在,我一定存在,我要最优解,最优解不可能是他~~!!!
那么还要他干嘛!!!直接–tail,带走他,。。。然后自己坐上他的位置。。
c[++tail]=i;

一波操作后就是这样一个过程,完成了维护,,,

贴代码

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
using namespace std;
int n,k,a[1000110],c[1000110];
int main(){
	std::ios::sync_with_stdio(false);
	cin>>n>>k;
	REP(i,1,n) cin>>a[i];a
	int head=1,tail=0; 
	REP(i,1,n){
		while(c[head]<=i-k&&head<=tail) head++;
		while(a[c[tail]]>a[i]&&tail>=head) tail--;
		c[++tail]=i;if (i>=k) cout<<a[c[head]]<<" ";}
	memset(c,0,sizeof(c));head=1; tail=0; cout<<endl;
	REP(i,1,n){
		while(c[head]<=i-k&&head<=tail) head++;
		while(a[c[tail]]<a[i]&&tail>=head) tail--;
		c[++tail]=i;if (i>=k) cout<<a[c[head]]<<" "; }
}

题目意思是自己看


下面是拓展

DP优化!!单调的上司~~

单调队列裸题,在洛谷中只有绿题难度,,,可是他却让无数dalao __orz__这是为啥
因为和DP这个坑扯上关系了!!!

下面开始讲 琪露诺

连接

首先看三秒题面。。DP。。土的不能再土的线性DP
然后5分钟代码。。两秒钟T飞。。。。

诶诶诶,咋回事。。。。

我们来看看DP 转移。。
DP[i]=max(DP[i-k])为能跳到这点的格子。。。
一个一个枚举。。。好暴力啊

再看看,再看看滑动窗口,你是否看到了那一个滑动的“窗口”!!

DP[i+1]与DP[i]的枚举范围只差两个,一个头,一个尾,头出去,尾进来。。然后又是一进一出,,又是。。。这不是滑动窗口吗!!!!!

下面就要你自己意会了,我已经尽力了。。这东西很玄学。。

贴代码
早期写的。。(有点丑)

#include<bits/stdc++.h>
using namespace std;
int dp[400010];
int n,r,l;
int head=1,tail=1;
int b[400010]; 
int a[400010];
int main(){
	memset(dp,0,sizeof(dp));
	std::ios::sync_with_stdio(false);
	cin>>n>>r>>l;
	for (int i=0; i<=n;++i){
		int x; cin>>x;
		a[i]=x;
	} b[1]=0;
	for (int i=r;i<=n+l;++i){
		while (head<=tail&&dp[b[tail]]<=dp[i-r]) --tail;
		b[++tail]=i-r;
		while (b[head]<i-l) ++head;
		dp[i]=dp[b[head]]+a[i];
	}
	int maxn=-0x7f7f7f7f;

	for (int i=n+1;i<=n+l;++i)
	maxn=max(dp[i],maxn);
		cout<<maxn<<endl;
} 

差不多了
来一句

@)!()NOIP RP==111111111111111

回头再来看都已经一年了

补几道题目

1.P2627 修剪草坪

首先列出dp方程式
dp的思路是,当我们已经选到了第i头奶牛可以得到的最大效率
而在这前i头奶牛中我们规定,一定是i-k—i中的一头奶牛可以休息,当然当i-k<0是就可以直接使DP[i]=sum[i]
所以
DP[i]=max(dp[j-1]-sum[j]+sum[i],dp[i])
其中的 (i-k<=j<=i)

然后我们发现方程式中的DP[j-1]和sum[j]是都是已知的,而且都是有顺序的往后移动,所以可以用单调队列来进行维护最大值
题解如下

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
#define MAXN 4000010
using namespace std;
void read(long long &x){
	x=0; char c=getchar(); int f=1; 
	for(;!isdigit(c);c=getchar()) if (c=='-') f=-f;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=f; 
}
long long n,k,s[MAXN],sum[MAXN],dp[MAXN],c[MAXN],head,tail;
long long num(int pla){
	return dp[pla-1]-sum[pla];
}
int main(){
	memset(dp,0,sizeof(dp)); 
//	freopen("1.in","r",stdin); 
	//	freopen("1.out","w",stdout); 
	read(n),read(k); 
	REP(i,1,n) read(s[i]),sum[i]=sum[i-1]+s[i];
//	REP(i,1,n) cout<<sum[i]<<"  "; cout<<endl; 
	head=1; tail=1; c[tail]=1; 
	REP(i,0,k) {
		dp[i-1]=sum[i-1];
		while (num(c[tail])<=num(i)&&tail>=head) tail--; ++tail;
	//	cout<<num(i)<<endl; 
		c[tail]=i; 
	}
//	cout<<c[head]<<endl; 
	REP(i,k,n+1) {
		while (c[head]<i-k) head++; 
	//	int pla1; 
		if (num(c[head])+sum[i]>dp[i]) {
			dp[i]=num(c[head])+sum[i];
	//		pla1=c[head]; 
		}
	//	cout<<c[head]<<endl;
		while (num(c[tail])<=num(i)&&tail>=head) tail--; ++tail;c[tail]=i;
	}
//	REP(i,n-100,n) cout<<dp[i]<<" "; cout<<endl; 
	cout<<dp[n]<<endl; 
}

2.花盆 flower

这题很水, 二分+单调队列,不解释

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
#define MAXN 100010
using namespace std;
void read(long long &x){
	x=0; char c=getchar(); int f=1;
	for(;!isdigit(c);c=getchar()) if (c=='-') f=-f;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=f;
}
struct node{
	long long x,y;
}point[MAXN];
bool cmp(node a,node b){
	return a.x<b.x; 
}
long long C[MAXN],n,d,l,r,c[MAXN];
long long check(long long lim){
//	cout<<lim<<endl; 
	long long head=1,tail=0; 
	long long Head=1,Tail=0; 
	REP(i,1,n) {
	while (point[c[tail]].y<=point[i].y&&head<=tail) --tail; c[++tail]=i;
	while (point[c[head]].x<point[i].x-lim) head++;
	while (point[C[Tail]].y>=point[i].y&&Head<=Tail) --Tail; C[++Tail]=i;
	while (point[C[Head]].x<point[i].x-lim) Head++;
//	cout<<point[c[head]].y-point[C[Head]].y<<endl;
		if (point[c[head]].y-point[C[Head]].y>=d) return 1;
	}
	return 0;
}
int main(){
//	freopen("测试数据.txt","r",stdin); 
	//	freopen("测试结果.txt","w",stdout); 
	read(n),read(d);
	REP(i,1,n) read(point[i].x),read(point[i].y); 
	sort(point+1,point+1+n,cmp);
	l=1; r=point[n].x+10;
	while (l<r) {
		 int mid=(l+r)>>1; 
		 if (check(mid)) r=mid; else l=mid+1;
	}
	if (l>point[n].x) cout<<-1<<endl; 
	else cout<<l<<endl;
	return 0;
}

翻译一下
2018NOIP RP++!!!
THE END

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
1. 智慧监狱概述 智慧监狱的建设背景基于监狱信息化的发展历程,从最初的数字化监狱到信息化监狱,最终发展到智慧监狱。智慧监狱强调管理的精细化、监管的一体化、改造的科学化以及办公的无纸化。政策上,自2017年以来,司法部连续发布了多项指导性文件,推动智慧监狱的建设。 2. 内在需求与挑战 智慧监狱的内在需求包括数据应用与共享的不足、安防系统的单一功能、IT架构的复杂性、信息安全建设的薄弱以及IT运维的人工依赖。这些挑战要求监狱系统进行改革,以实现数据的深度利用和业务的智能化。 3. 技术架构与设计 智慧监狱的技术架构包括统一门户、信息安全、综合运维、安防集成平台和大数据平台。设计上,智慧监狱采用云计算、物联网、大数据和人工智能等技术,实现资源的动态分配、业务的快速部署和安全的主动防护。 4. 数据治理与应用 监狱数据应用现状面临数据分散和共享不足的问题。智慧监狱通过构建数据共享交换体系、数据治理工具及服务,以及基于数据仓库的数据分析模型,提升了数据的利用效率和决策支持能力。 5. 安全与运维 智慧监狱的信息安全建设涵盖了大数据应用、安全管理区、业务区等多个层面,确保了数据的安全和系统的稳定运行。同时,综合运维平台的建立,实现了IT系统的统一管理和自动化运维,提高了运维效率和系统的可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值