ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

C++类型题整理

2022-01-18 11:32:45  阅读:151  来源: 互联网

标签:斜率 队列 sumT C++ 类型 dp 整理 sumC 单调


【营业记录】时光流韵

 

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


关键词: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​。

考虑一些问题:

你需要注意:

  • 使用单调队列的条件是斜率满足单调性质而不是点满足单调性质,如果不满足单调性质可以换用单调栈内二分,复杂度多一个 \loglog。

  • 由于两个点的 xx 坐标有可能相同所以需要特判成 \text{inf}inf,如果不特判也许会变成 \text{-inf}-inf。

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有