当前位置:网站首页>1D/1D动态规划学习总结
1D/1D动态规划学习总结
2022-04-23 06:21:00 【Zimba_】
前言:
自从发现1D/1D动态规划这个新的技能块后,每次看到这类题目都能口嗨成功,然后队友催我赶紧学。再者发现有好多优化方法,所以慢慢学,每天学一点,加上还要做一些奇奇怪怪的题目保持状态,也就差不多了。
第0天(1D/1D动态规划)
什么是1D/1D动态规划?
1D/1D动态规划是指形如 d p i = m a x { d p j + w ( i , j ) } dp_{i}=max\{ dp_{j}+w(i,j)\} dpi=max{
dpj+w(i,j)}的动态规划,当然 m a x max max也可以换成 m i n min min。
据说长这样的 d p dp dp一般都可以优化到 o ( n l o g n ) o(nlogn) o(nlogn)。
优化的思路就是在 j j j的范围中,快速找到一个最优的转移点。
学习思路大概就是学习这个转移方程的不同类型,然后对于每种类型学习它的优化方法。
学习链接(多去走走,这样笔者偷完知识就不会愧疚了,博客里会加入自己的东西(也许))
第0.5天(前缀和优化)
有个前缀和优化,这个笔者默认会了,就不学了。不会的话请读者自行学习。
第1天(单调队列优化)
啊~新的力量!
今天是单调队列优化dp,当然,就当大家都会单调队列了。
实话说,学完感觉原来的 d p dp dp式也有点不恰当,我们把 d p j dp_{j} dpj也看作是 w ( i , j ) w(i,j) w(i,j)中 j j j的一部分运算,那式子其实就是 d p i = m a x { w ( i , j ) } dp_{i}=max\{ w(i,j)\} dpi=max{
w(i,j)}。
好了,进入今天的正题。
按照我们的学习思路,我们应该对于此类优化,有一个归纳的转移式子,那就有了。
转移方程
d p i = m a x { w ( j ) } dp_{i}=max\{ w(j)\} dpi=max{ w(j)}
说明
这个方程的意思就是 d p dp dp的转移只和 j j j有关。然后这个 j j j呢,一般是一个区间范围,并且随着 i i i的变大, j j j的范围区间是整体右移的。这样可以保证它能在转移过程中,用单调队列找到最优点。
思路:
说明中提到 j j j的范围区间整体右移,我们设转移 d p i − 1 dp_{i-1} dpi−1时 j j j的范围区间是 [ l i − 1 , r i − 1 ] [l_{i-1},r_{i-1}] [li−1,ri−1],转移 d p i dp_{i} dpi时 j j j的范围区间是 [ l i , r i ] [l_{i},r_{i}] [li,ri]。那么我们 r r r变大的时候,就把新的元素加入到单调队列右边(如果要加的话,在这之前还要把右边一些不要的元素拿走),然后答案在队列左边找到范围内的。当然,这都是单调队列的东西,就不细讲了。
实战例题
题意
n n n棵树,每棵树有一个高度 d i d_{i} di,小 H H H在第 1 1 1棵树上,要跳到第 n n n棵树上。在第 i i i棵树上时,可以跳到第 i , i + 1 , … , i + k i,i+1,…,i+k i,i+1,…,i+k棵树上。每跳一次,如果高度不小于原来的高度,疲劳值就会加 1 1 1。现在问小 H H H从第 1 1 1棵树上跳到第 n n n棵树上,最少的疲劳值是多少。
( 1 ≤ n ≤ k ≤ 1 0 6 ) (1\leq n\leq k\leq 10^{6}) (1≤n≤k≤106)
思路
我们设状态 d p i dp_{i} dpi表示跳到第 i i i棵树上需要花费的最少疲劳值。
那么有转移方程 d p i = m i n { d p j + [ d i > = d j ] } dp_{i}=min\{dp_{j}+[d_{i}>=d_{j}]\} dpi=min{
dpj+[di>=dj]},其中 j ∈ [ i − k , i − 1 ] j\in [i-k,i-1] j∈[i−k,i−1]。
我们要学习这个思路。
首先我们观察 j ∈ [ i − k , i − 1 ] j\in [i-k,i-1] j∈[i−k,i−1],我们发现区间随着 i i i变大整体右移,成立。
然后 d p j + [ d i > d j ] dp_{j}+[d_{i}>d_{j}] dpj+[di>dj],它是一个关于 i i i和 j j j的式子,呃好像不成立。
这时候只能发挥我们的聪明才智了,咳咳。
我们先发现 d p i dp_{i} dpi单调不下降,且每次增加都是加 1 1 1。所以对于两个 d p j dp_{j} dpj的值,我们肯定直接取小的那个,因为小的那个 + 1 +1 +1也不会超过大的。然后对于两个相同的 d p j dp_{j} dpj,我们一定优先取 d j d_{j} dj大的那个,也就是要在范围内找使得 ( − d p j , d j ) (-dp_{j},d_{j}) (−dpj,dj)最大的 j j j。这样就可以用单调队列优化了。
刚才演示翻车了,我决定用 d p i = w ( i , j ) , w h e r e g ( j ) i s m a x f o r j i n r a n g e dp_{i}=w(i,j),where\;g(j)\;is\;max\;for\;j\;in\;range dpi=w(i,j),whereg(j)ismaxforjinrange来作为新式子。
这样也许就可以泛化模型了。
代码
题目有点卡,开o2优化就过了。
写完下班。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
int q[1000005],fr,bk;
int d[1000005];
int dp[1000005];
int w(int i,int j)
{
return dp[j]+(d[i]>=d[j]);
}
P g(int j)
{
return P(-dp[j],d[j]);
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&d[i]);
int m;
scanf("%d",&m);
while(m--)
{
fr=bk=0;
int k;
scanf("%d",&k);
dp[1]=0;
q[bk++]=1;
for(int i=2;i<=n;i++)
{
while(q[fr]<i-k)fr++;
dp[i]=w(i,q[fr]);
while(!(fr==bk)&&g(q[bk-1])<=g(i))bk--;
q[bk++]=i;
}
printf("%d\n",dp[n]);
}
return 0;
}
第二天(分治优化)
(待续。。)
(upd:更新了标题后面不应该加冒号的bug)
昨天是关于 j j j独立的转移,依据 j j j的单调性用单调队列优化。
那么今天这个是不关于 j j j独立,即和 i i i和 j j j都有关的,刚学了一种分治优化,当然你得先会分治。
转移方程
d p i = m a x ( w ( i , j ) ) , j ∈ [ 1 , i − 1 ] dp_{i}=max(w(i,j)),j \in [1,i-1] dpi=max(w(i,j)),j∈[1,i−1]
又或者说 d p i = w ( i , j ) , w h e r e g ( i , j ) i s m a x f o r j i n r a n g e dp_{i}=w(i,j),where\;g(i,j)\;is\;max\;for\;j\;in\;range dpi=w(i,j),whereg(i,j)ismaxforjinrange
并且还需要它的转移,随着 i i i满足某种决策单调性。
说明
总之,我们需要找的最优转移点和 i , j i,j i,j都有关。
然后,转移要随着 i i i满足决策单调性。举个例子,大致意思是,当 i i i 慢慢变大,最优决策点 j j j也会慢慢变大。但这不意味着 j j j大更优,有些 j j j压根不会被作为最优决策点,只是哪些对每个 i i i作为最优决策点的 j j j们是随着 i i i变大而变大的(不下降)。
思路
分治的大致做法是,我们先找中间的 d p m i d dp_{mid} dpmid的转移点 q m i d qmid qmid。然后根据决策单调性,当 i < m i d i<mid i<mid时,转移点 j ∈ [ 1 , q m i d ] j\in[1,qmid] j∈[1,qmid];当 i > m i d i>mid i>mid时,转移点 j ∈ [ q m i d , n ] j\in[qmid,n] j∈[qmid,n]且 j < i j<i j<i。
然后每次这样取中点求最优转移点,把区间割成两个一半。递归深度是 l o g log log级的,那我们找转移点就暴力遍历可能存在最优转移点的决策区间。总体复杂度 o ( n l o g n ) o(nlogn) o(nlogn)。
实战例题
题意
给定一个长度为 n n n的序列 { a n } \{a_{n}\} {
an},对于每个 i ∈ [ 1 , n ] i\in [1,n] i∈[1,n],求出一个最小的非负整数 p p p,使得 ∀ j ∈ [ 1 , n ] \forall j\in[1,n] ∀j∈[1,n],都有 a j ≤ a i + p − ∣ i − j ∣ a_j\le a_i+p-\sqrt{|i-j|} aj≤ai+p−∣i−j∣
1 ≤ n ≤ 5 × 1 0 5 , 0 ≤ a i ≤ 1 0 9 1\leq n\leq 5\times 10^{5},0\leq a_{i}\leq 10^{9} 1≤n≤5×105,0≤ai≤109
思路
我们令 d p i dp_{i} dpi表示第 i i i个答案。
首先,把绝对值处理掉,我们可以正向跑一遍 d p dp dp,再逆向跑一遍,求个最大值。
那么正向的转移方程就是 d p i = m a x { a j − a i + i − j } , j ∈ [ 1 , i − 1 ] dp_{i}=max\{a_{j}-a_{i}+\sqrt{i-j}\},j\in [1,i-1] dpi=max{
aj−ai+i−j},j∈[1,i−1]。
关于 i i i常量先提出来,变成 d p i = m a x { a j + i − j } − a i , j ∈ [ 1 , i − 1 ] dp_{i}=max\{a_{j}+\sqrt{i-j}\}-a_{i},j\in [1,i-1] dpi=max{
aj+i−j}−ai,j∈[1,i−1]。
那么现在变成了,我们所说的模范转移式 d p i = m a x { w ( i , j ) } dp_{i}=max\{w(i,j)\} dpi=max{
w(i,j)},其中 w ( i , j ) = a i + i − j w(i,j)=a_{i}+\sqrt{i-j} w(i,j)=ai+i−j。
然后,它要满足我们所说的决策单调性,即 i i i变大的时候,我们选取的最优决策点 j j j也会慢慢变大。我们观察式子 w j ( i ) = a i + i − j w_{j}(i)=a_{i}+\sqrt{i-j} wj(i)=ai+i−j是一个关于 i i i的函数,有 i − 1 i-1 i−1个这样的函数,我们要对于每个 i i i
选取一个最佳的函数 w j w_{j} wj,使得函数值最高。
然后我们发现这些函数的增长速度都是慢慢变慢的,且 w j w_{j} wj比 w j + 1 w_{j+1} wj+1在 i i i相同时增长速度要慢。也就是一旦 i i i增长到某个时候 w j w_{j} wj被一个 w k w_{k} wk超过了,且 k > j k>j k>j,那么 w j w_{j} wj将永无翻身之日。
总的来说,就是满足当 i i i变大,最优决策点只会不断变大(或者换个题变小)。
满足我们前面所说的性质,然后就可以分治搞了。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
double a[500005];
ll dp1[500005],dp2[500005];
double w(int i,int j)
{
return a[j]+sqrt(abs(i-j))-a[i];
}
void solve(int l,int r,int ql,int qr,ll *dp)
{
if(l>r||ql>qr)return ;
int mid=(l+r)/2;
int qmid=ql;
for(int i=ql;i<=qr&&i<mid;i++)if(w(mid,i)>w(mid,qmid))qmid=i;
dp[mid]=ceil(w(mid,qmid));
solve(l,mid-1,ql,qmid,dp);solve(mid+1,r,qmid,qr,dp);
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lf",&a[i]);
solve(1,n,1,n,dp1);
reverse(a+1,a+n+1);
solve(1,n,1,n,dp2);
reverse(dp2+1,dp2+n+1);
for(int i=1;i<=n;i++)printf("%lld\n",max(0ll,max(dp1[i],dp2[i])));
return 0;
}
第N天
(待续。。)
拿金了,先断更了
版权声明
本文为[Zimba_]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_43785386/article/details/110824476
边栏推荐
- 青龙面板拉库命令更新【2022/4/20】收藏不走丢
- 免费开源农业物联网云平台(Version:3.0.1)
- van-uploader上传图片实现过程、使用原生input实现上传图片
- 自组网灵活补盲|北峰油气田勘测解决方案
- golang实现一个带Web界面的五险一金计算器
- el-date-picker中自定义快捷选项picker-options,动态设置禁用日期
- How does the public and Private Integrated walkie talkie realize cooperative work under multi-mode communication?
- 字节跳动2020秋招编程题:根据工号快速找到自己的排名
- remote: Support for password authentication was removed on August 13, 2021.
- Tensorflow安装后ImportError: DLL load failed: 找不到指定的模块,且国内安装缓慢
猜你喜欢
How does the public and Private Integrated walkie talkie realize cooperative work under multi-mode communication?
组合数求解与(扩展)卢卡斯定理
Tensorflow安装后ImportError: DLL load failed: 找不到指定的模块,且国内安装缓慢
城市应急管理|城市突发事故应急通信指挥调度系统
可视化之路(十)分割画布函数详解
不需要破解markdown编辑工具Typora
学习资料
SDC intelligent communication patrol management system of Nanfang investment building
免费开源充电桩物联网云平台
利用mysql-binlog恢复数据
随机推荐
vim+ctags+cscpope开发环境搭建指南
枫桥学院开元名庭酒店DMR系统解决方案
启动mqbroker.cmd失败解决方法
JDBC连接池
连接orcale
PyTorch 17. GPU concurrency
PyTorch 19. Differences and relations of similar operations in pytorch
自定义classloader并实现热部署-使用loadClass
javscript获取文件真实后缀名
安装tui-editor失败,快速解决方案
xdotool按键精灵
USO technology was invited to share the technical framework and challenges of AI synthetic virtual characters at lvson2020 conference
可视化常见绘图(一)堆叠图
Solution of emergency communication system for major security incidents
LATEX公式注意事项
推导式与正则式
通用型冒泡、选择、插入、希尔、快速排序的代码实现
Emergency air space integrated communication system scheme of Guangxi Power Grid
ES6之箭头函数细谈
Metro wireless intercom system