Loading

算法学习笔记【8】| 单调队列优化DP

单调队列:就是滑动窗口,可以求出定长 RMQ,时间复杂度线性。

优化 DP

首先把dp方程写成这个样子:

f[i]=max(或者其他运算){f[j]+calc(i,j)}<script type="math/tex;mode=inline" id="MathJax-Element-3">f[i] = max(\text{或者其他运算})\{f[j]+calc(i,j)\}</script>f[i] = max(\text{或者其他运算})\{f[j]+calc(i,j)\}

注意 calc 的计算要能只和 j 有关。

同时 j 满足一个定长的范围,也就是说转移是从一个定长区间来的。

P3572 [POI2014] PTA-Little Bird,P1725 琪露诺:完全的模版。

在此不妨以P3957 [NOIP2017 普及组] 跳房子为例。

首先意识到本题答案满足单调性,灵活性的定义也符合人类想象(越灵活,可能得到的答案就越大),于是首先二分 k 的值,然后考虑 k 确定时的做法。

根据题目灵活性的定义,实际上能跳的范围就是 [max(dg,1),d+g]<script type="math/tex;mode=inline" id="MathJax-Element-1">[\max(d-g,1),d+g]</script>[\max(d-g,1),d+g] ,满足定长区间, f[i]=f[j]+s[i]<script type="math/tex;mode=inline" id="MathJax-Element-2">f[i]=f[j]+s[i]</script>f[i]=f[j]+s[i] ,那么使用单调队列。

结合一下我的代码:

#include <bits/stdc++.h>
#define int long long
#define U (l+r)/2
#define F(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
const int N=500005,M=(N<<1),inf=1e18;
int n,d,k,x[N],s[N],f[N],q[M];
int ck(int g){
	int h=1,t=0,l=0;
	fill(f+1,f+n+1,-inf);
	F(i,1,n){
		while(h<=t&&x[i]-x[q[h]]>d+g) h++;
		while(l<i&&x[i]-x[l]>=d-g){
			while(h<=t&&f[l]>=f[q[t]])t--;
			q[++t] = l++;
		}if(h<=t)f[i]=f[q[h]]+s[i];
		if(f[i]>=k) return 1;
	}return 0;
}
signed main(){
	ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>d>>k;F(i,1,n) cin>>x[i]>>s[i];
	int l=0,r=1e9,ans=-1;while(l<=r)
		ck(U)?ans=U,r=U-1:l=U+1;
	cout<<ans; return 0;
}

首先,对超出范围的,进行删除,然后对符合条件的,进行加入,最后直接转移答案。

CF372C Watching Fireworks is Fun

这题有点启示作用,当 k 满足 jx<k<j<script type="math/tex;mode=inline" id="MathJax-Element-4">j-xj-x<k<j 的时候显然直接单调队列。然而这道题会推出来一个这样的式子: jx<k<j+x<script type="math/tex;mode=inline" id="MathJax-Element-9">j-xj-x<k<j+x 。做法是拆成两段,一段满足 jx<kj<script type="math/tex;mode=inline" id="MathJax-Element-5">j-xj-x<k\le j ,另一段是 jk<j+x<script type="math/tex;mode=inline" id="MathJax-Element-6">j\le kj\le k<j+x ,可以两次单调队列秒掉。

具体的,我们把状态设为 f[i][j]<script type="math/tex;mode=inline" id="MathJax-Element-8">f[i][j]</script>f[i][j] ,表示放第 i 个烟花的时候在 j 点能得到的最大值, f[i][j]=max{f[i1][k]+b[i]|a[i]j|}<script type="math/tex;mode=inline" id="MathJax-Element-7">f[i][j]=\max\{f[i-1][k]+b[i]-\left| a[i]-j \right|\}</script>f[i][j]=\max\{f[i-1][k]+b[i]-\left| a[i]-j \right|\} ,同时 k 能走到 j 点(即定长区间的最值)。

P2569 [SCOI2010] 股票交易:

转移多一点的时候只需要去先写朴素的方程,然后优化。

注意,尽量使用数组代替deque,因为这个东西很慢(虽然有些题实测差距不大)。

posted @ 2023-12-23 20:58  紊莫  阅读(25)  评论(0)    收藏  举报  来源