单调队列优化DP

单调队列优化DP

先上
滑动窗口/单调队列模板

单调队列顾名思义,队列中的元素是具有单调性的
维护时需要队首l与队尾r移动来更新
超出范围的弹掉,要加入的加到自己该在的位置
具体实现看一下下面这组例子

先让需要维护的区间长度为k=3,维护每一个区块的最大值,deque的l到r,保持单调递减
原序列 3 2 1 4 5 2
1. [3 2 1] 4 5 2
   3是第一个数,先把位置入队l++
   2,1都小于队列中的3,使队列单调下降,直接放入
2. 3 [2 1 4] 5 2
   3已经不在区间里了,l++把它位置弹掉
   4显然比此时的队尾元素1大,也比2大,不断r--,放到它该在的队列位置
3. 3 2 [1 4 5] 2
   同理...看代码
4. 3 2 1 [4 5 2]
   同理...看代码

单调队列维护的是队列中元素的位置,并保证单调性
输出的时候区间最大值就是此时对应的队列的队头存的位置对应的元素

代码 luogu滑动窗口/单调队列模板


#include <bits/stdc++.h>
using namespace std;

const int N=1e6+10;

int read() {

	int x=0,f=1;
	char ch=getchar();
	while(ch<48||ch>57) {
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>=48&&ch<=57) {
		x=(x<<3)+(x<<1)+(ch^48);
		ch=getchar();
	}
	return x*f;

}

int n,m;
int q1[N],q2[N];
int a[N];

int main() {

	n=read(),m=read();

	for(int i=1; i<=n; i++) a[i]=read();

	int h=1,t=0;
	for(int i=1; i<=n; i++) {

		while(h<=t&&q1[h]+m<=i) h++;
		while(h<=t&&a[i]<a[q1[t]]) t--;
		q1[++t]=i;
		if(i>=m) printf("%d ",a[q1[h]]);

	}

	printf("\n");
	h=1,t=0;
	for(int i=1; i<=n; i++) {

		while(h<=t&&q2[h]+m<=i) h++;
		while(h<=t&&a[i]>a[q2[t]]) t--;
		q2[++t]=i;
		if(i>=m) printf("%d ",a[q2[h]]);

	}
	return 0;

}

接着再是
单调队列优化DP
这篇两篇写得很好 https://www.cnblogs.com/superPG/p/14985576.html
https://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html
emm
其实单调队列就是一种队列内的元素有单调性(单调递增或者单调递减)的队列,
最优解存在队首,而队尾则是最后进队的元素。
用来维护区间最值或者降低DP的维数减少空间及时间
利用单调队列对dp方程进行优化,可将O(n)复杂度降至O(1)
N维的DP,可以优化为N-1维 !!!

单调队列适合优化决策取值范围的上、下界均单调变化的问题
并不是所有DP都可以由单调队列优化
像最大化、最小化决策的结果,即决策具有单调性的题目可以优化

CF1077F2 Pictures with Kittens (hard version)


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll N=5e3+10;
const ll inf=1e18;

ll read() {
	
	ll x=0,f=1;
	char ch=getchar();
	while(ch<48||ch>57) {
		if(ch=='-') f=-1;
		ch=getchar(); 
	}
	while(ch>=48&&ch<=57) {
		x=(x<<3)+(x<<1)+(ch^48);
		ch=getchar();
	}
	return x*f;
	
}

ll n,k,m;
ll a[N],ans=-1;
ll dp[N][N],q[N];

/*

dp[i][j]表示前i个数中选了j个,第i个必取,可得到的最大和 
dp[i][j]=max(dp[u][j-1]+a[i]); 

*/

int main() {
	
	n=read();
	k=read();
	m=read();
	
	for(ll i=0;i<=n;i++) {
		
		for(ll j=0;j<=m;j++) {
			
			dp[i][j]=-inf;
			
		}
		
	}
	
	for(ll i=1;i<=n;i++) {
		
		a[i]=read();
		
	}	
	
	dp[0][0]=0;
	for(ll j=1;j<=m;j++) {
		
		ll l=1,r=1;
		q[r]=0;
		for(ll i=1;i<=n;i++) {
			
			while(l<=r&&q[l]+k<i) l++;
			dp[i][j]=dp[q[l]][j-1]+a[i];
			while(l<=r&&dp[i][j-1]>=dp[q[r]][j-1]) r--;
			q[++r]=i;
			
		}
		
	}
	
	for(ll i=n-k+1;i<=n;i++) ans=max(ans,dp[i][m]);
	printf("%lld\n",ans);
	
	return 0;
}

posted @   Diamondan  阅读(376)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示