编程语言
首页 > 编程语言> > C++类型题整理

C++类型题整理

作者:互联网

【营业记录】时光流韵

 

也许是一枚能够跨越时空的护符,是先闻其声,或是余音绕梁。


关键词:DP

(1) 内容概要

(2) 费用提前计算

让我们以 P2365 任务安排 为例。

下文中题目里的费用系数我使用 c_ici​ 表示。且令 sunTsunT 为 tt 的前缀和,sumCsumC 为 cc 的前缀和。

我们令 f[i,j]f[i,j] 表示把前 ii 个任务分为 jj 批的最小费用。得出:

f[i,j]=min_{0\le k<i}\{f[k,j-1]+(s*j+sumT[i])*(sumC[i]-sumC[k])\}f[i,j]=min0≤k<i​{f[k,j−1]+(s∗j+sumT[i])∗(sumC[i]−sumC[k])}

这样是 O(n^3)O(n3)。

然后考虑一个思想费用提前计算。我们发现每次转移时 jj 的存在意义是算出 s*js∗j 然后算出时间。然后我们把这个费用提前,在每次转移的时候为以后的费用考虑好,机器为这次启动花费的 ss 会累加到后面。所以现在可以舍去一维,令 f[i]f[i] 表示把前 ii 个任务分成若干批最小费用,得:

f[i]=min_{0\le j<i}\{f[j]+(sumC[i]-sumC[j])*sumT[i]+s*(sumC[N]-sumC[j])\}f[i]=min0≤j<i​{f[j]+(sumC[i]−sumC[j])∗sumT[i]+s∗(sumC[N]−sumC[j])}

这是 O(n^2)O(n2)。然后这题暂时是做完了。简单的小练习:

(3) 单调栈

单调栈,就是要维护一个栈,单调递增或递减。一般地,放入元素时,如果栈顶的元素不符合单调递增(或递减),那就把它弹出,再查栈顶。直到符合或栈空了,再把元素放入。

线性求每个数两侧比它大(或小)的数的位置。

典型的应用是 柱状图中最大的矩形,用单调栈维护每个数两侧比它小的数的位置。

P2422 良好的感觉 同理,只需要一个前缀和优化,就是单调栈的模板题了。

(4) 单调队列

单调队列,就是要维护一个队列,单调递增或递减。一般地,放入元素时,先弹出末尾无效元素。如果队列首的元素不符合单调递增(或递减),那就把它弹出,再查队列首。直到符合或队列空了,再把元素放入。

队列中的元素其对应在原来的列表中的顺序必须是单调递增的。队列中元素的大小必须是单调递增减/甚至是自定义也可以)。

f(x)=opt_{i=bound[x]}^{x-1}(const[i])f(x)=opti=bound[x]x−1​(const[i])

对于这样一类动态规划问题,我们可以运用单调队列来解决: 其中 bound[x]bound[x] 随着 xx 单调不降,而 const[i]const[i] 则是可以根据 ii 在常数时间内确定的唯一的常数。

(5) 斜率优化

斜率优化要解决的问题是求 dp[i]=a[i]*b[j]+c[i]+d[j]dp[i]=a[i]∗b[j]+c[i]+d[j]。在最基础的形式中,a[i],b[j]a[i],b[j] 单调递增。如果 a[i]a[i] 不单调递增也没事,我们之后再讨论。

移项:-a[i]b[j]+dp[i]-c[i]=d[j]−a[i]b[j]+dp[i]−c[i]=d[j]。

把 b[j]b[j] 看作 xx ,把 d[j]d[j] 看作 yy,把 -a[i]−a[i] 看作 kk

kx+dp[i]-c[i]=ykx+dp[i]−c[i]=y此直线过 (x,y)(x,y) 且斜率已知为 kk ,要求截距最小。维护一个凸壳。对于每个 ii,要求的是第一个满足 slope(P_j,P_{j+1})>kslope(Pj​,Pj+1​)>k 的。

一般斜率优化单调队列维护即可。若 aa 无单调性,用二分查找;若 bb 无单调性,用平衡树。

回到 P2365 任务安排,发现这个式子符合斜率优化。

f[i]=f[j]+(sumC[i]-sumC[j])*sumT[i]+s*(sumC[N]-sumC[j])f[i]=f[j]+(sumC[i]−sumC[j])∗sumT[i]+s∗(sumC[N]−sumC[j])

f[i]=f[j]+sumC[i]*sumT[i]-sumC[j]*sumT[i]+sumC[N]*s-sumC[j]*sf[i]=f[j]+sumC[i]∗sumT[i]−sumC[j]∗sumT[i]+sumC[N]∗s−sumC[j]∗s

f[i]=-sumC[j]*sumT[i]+(f[j]-sumC[j]*s)+(sumC[i]*sumT[i]+sumC[N]*s)f[i]=−sumC[j]∗sumT[i]+(f[j]−sumC[j]∗s)+(sumC[i]∗sumT[i]+sumC[N]∗s)

(f[i]-sumC[i]*sumT[i]-sumC[N]*s)+sumC[j]*sumT[i]=f[j]-sumC[j]*s(f[i]−sumC[i]∗sumT[i]−sumC[N]∗s)+sumC[j]∗sumT[i]=f[j]−sumC[j]∗s

b+kx=yb+kx=y

操作步骤如下:

  1. 对于队首 while(slope(P_{head},P_{head+1})<k) head++while(slope(Phead​,Phead+1​)<k)head++;

  2. 队首为最优,计算 dp[i]dp[i];

  3. 队尾 while(slope(P_{tail-1},P_{tail})>slope(P_{tail-1},P_{i})) tail--while(slope(Ptail−1​,Ptail​)>slope(Ptail−1​,Pi​))tail−−;

  4. 在队尾加入 P_iPi​。

考虑一些问题:

你需要注意:

标签:斜率,队列,sumT,C++,类型,dp,整理,sumC,单调
来源: https://www.cnblogs.com/wangruidong03/p/15817027.html