【总结】【模板】2021年寒假集训前期总结

本文介绍了如何利用单调队列和斜率优化策略来简化动态规划问题。首先,通过单调队列优化求解区间最小值问题,然后通过斜率比较法处理转移状态的优先级选择。通过实例演示了这两种方法在实际问题中的应用和模板化处理过程。
摘要由CSDN通过智能技术生成

0x00 前言

学了 DP 的优化和搜索的优化,说实话真的没什么可以总结的,但是还是强行安了几个模板,有的版块确实总结不出什么模板了,就暂且咕咕咕掉吧。。。

0x01 单调队列优化 DP

首先还是要写出 dp 式

一般是一个两重循环

第一重循环枚举阶段,第二重循环枚举前面的并更新后面

大致就是这样的:

d p i = m i n ( j − k + 1 ) → j { f ( i ) + g ( j ) } dp_i=\mathrm{min}_{(j-k+1)\to j}\{f(i)+g(j)\} dpi=min(jk+1)j{f(i)+g(j)}

其中 f ( i ) f(i) f(i) g ( j ) g(j) g(j),分别表示的是与 i i i j j j 有关的多项式/单项式

至于 k k k,由题目而定

由于 i i i 在这个阶段是固定的,所以把 f ( i ) f(i) f(i) 给分离出来

d p i = m i n ( j − k + 1 ) → j { g ( j ) } + f ( i ) dp_i=\mathrm{min}_{(j-k+1)\to j}\{g(j)\}+f(i) dpi=min(jk+1)j{g(j)}+f(i)

然后对于 g ( i ) g(i) g(i),即是求一个区间最小

直接套用单调队列就行了

int head=1,tail=1;
for(int i=1;i<=n;i++){
	if(i-k>q[head] && head<=tail){
		head++;
	}
	while(g(i)>g(q[tail]) && head<=tail){//这里的g(i)按题而定
		tail--;
	}
	用q[head]当做原本的j去更新dp[i];
	q[++tail]=i;
}

0x02 斜率优化 DP

推柿子是有点复杂,但最后套用模板是挺方便

由于题目千遍万化,没有办法直接表示,所以就以一道版题为例:

打印文章:

  • 首先,写出dp式: d p i = m i n ( d p j + ( s u m i − s u m j ) 2 + m ) dp_i=min(dp_j+(sum_i-sum_j)^2+m) dpi=min(dpj+(sumisumj)2+m) s u m i sum_i sumi 表示的是前缀和

  • 然后不妨设 k k k转移过劳 比 j j j 转移过来优,且 k k k j j j 后面


  • d p j + ( s u m i − s u m j ) 2 + m ≥ d p k + ( s u m i − s u m k ) 2 + m dp_j+(sum_i-sum_j)^2+m \geq dp_k+(sum_i-sum_k)^2+m dpj+(sumisumj)2+mdpk+(sumisumk)2+m

  • 然后对上面这个不等式化简,注意 k k k j j j 后,所以 s u m k ≥ s u m j sum_k \geq sum_j sumksumj

  • ( s u m k 2 + d p k ) − ( s u m j 2 + d p j ) s u m k − s u m j ≥ 2 s u m i \frac{(sum_k^2+dp_k)-(sum_j^2+dp_j)}{sum_k-sum_j} \geq 2sum_i sumksumj(sumk2+dpk)(sumj2+dpj)2sumi

于是上面这个是分子,下面这个是分母,右边那个是R(i),盲猜是维护一个下凸壳(因为如果不是的话是无法套这个模板的)

#include<cstdio>
#include<iostream>
#include<cmath> 
using namespace std;
const int MAXN=500005;
int n,m,q[MAXN],s[MAXN],dp[MAXN];
int getson(int k,int j){
	//do sth.
}
int getmon(int k,int j){
	//do sth.
}
int main(){
	int n,m;
	//init 
    int head=1,tail=1;
    for(int i=1;i<=n;i++){
    	while(head<tail && getson(q[head],q[head+1])<=R(i)*getmon(q[head],q[head+1])){
    		head++;
		}
		dp[i]=dp[q[head]]+(s[i]-s[q[head]])*(s[i]-s[q[head]])+m;
		//这里使用q[head]当作暴力中的j去更新dp[i]
		while(head<tail && getson(q[tail],i)*getmon(q[tail-1],q[tail])<=getson(q[tail-1],q[tail])*getmon(q[tail],i)){
			tail--;
		}
		q[++tail]=i;
	}
    printf("%d\n",dp[n]);
return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值