[学习笔记] 斜率优化DP - DP
这个真的好容易啊 ——wzw
斜率优化dP
例题
[SDOI2012] 任务安排
毒瘤题,让我惨淡经营了两天。这道题luogu有简单版,可以先去看简单版。
显然这是一只DP题,直接开始推狮子。令 dp[i]
表示以第 \(i\) 个任务为终止时的最小花费。\(t\) 和 \(w\) 都表示的是前缀和,那么有 \(dp[i]=min\{dp[j]+(t[i]+S*k)(w[i]-w[j])\}\) 。观察狮子,\(k\) 是完全不知道的,也就是转移所依赖的 dp[j]
是否有分组是完全不知道的,所以无法转移。不妨继续思考,假如在第 \(1\sim i\) 分了一组,那么 \(i+1\sim n\) 中必然也会分组,也就是 \(s+1\sim n\) 的时间必然会增加一个 \(S\)。于是有:
dp[i]&=min\{dp[j]+t[i]*(w[i]-w[j])+S*(s[n]-s[i])\} \\
&=min\{dp[j]-t[i]*w[j]\}+t[i]*w[i]+S*(s[n]-s[i])\\
&j\in [0, i-1]
\end{split}
\]
观察min中的狮子,把它们单独拎出来:\(dp[j]=dp[i]+t[i]*w[j]\)。其中 \(dp[i]\) 为截距,\(t[i]\) 为斜率,只需要用单调队列维护一个下凸包即可。但考虑到 \(Ti\) 存在负数,那么斜率就不再具有单调性,用二分维护即可。复杂度 \(O(nlogn)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 10;
int n, s, t[N], c[N], p[N], dp[N], tail;
inline int up(int a, int b){ return dp[b] - dp[a]; }
inline int dn(int a, int b){ return c[b] - c[a]; }
inline int get(int l, int r, int slp){
while(l < r){
int mid = (l + r) >> 1;
if(up(p[mid], p[mid+1]) <= slp*dn(p[mid], p[mid+1])) l = mid + 1;
else r = mid;
} return l;
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>s;
for(int i=1, j; i<=n; ++i){
cin>>t[i]>>c[i];
t[i] += t[i-1], c[i] += c[i-1];
}
for(int i=0, j; i<=n; ++i){
j = get(1, tail, t[i]);
j = p[j];
dp[i] = dp[j] + t[i]*(c[i]-c[j]) + s*(c[n]-c[i]);
while(tail > 1 && up(p[tail-1], p[tail])*dn(p[tail], i) >= up(p[tail], i)*dn(p[tail-1], p[tail])) --tail;
p[++tail] = i;
} return cout<<dp[n], 0;
}
注意几点:
- 维护时应严格递增,不要留有斜率相等的点。
- 维护时应看准 \(tail\) 的范围,如果涉及到 \(tail-1\),那么 \(tail>1\)
- 因为涉及double,尽量不要直接把斜率算出来,而是使用up和down。
[APIO2010] 特别行动队
当你打完任务安排后就会发现这道题简单的跟1+1似的。
一只小地痞。令 dp[i]
表示以第 \(i\) 名士兵为界限分组时的最大战斗力值。\(w\) 表示前缀和,令 \(W=w[i]-w[j]\)。于是有:
\]
展开后得:
\]
观察到只有max里面的狮子和 \(j\) 有关,于是提出来并变形得:
\]
显然 \(2a*w[i]\) 为斜率。因为需要求max,所以维护一个上凸包(斜率严格递减)。接着就是斜率地痞板子了。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 1;
int n, a, b, c, w[N], dp[N], p[N], tail, head = 1;
inline int up(int x, int y){ return dp[y] + a*w[y]*w[y] - b*w[y] - dp[x] - a*w[x]*w[x] + b*w[x]; }
inline int dn(int x, int y){ return w[y] - w[x]; }
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>a>>b>>c;
for(int i=1; i<=n; ++i) cin>>w[i], w[i] += w[i-1];
for(int i=0; i<=n; ++i){
while(head < tail && up(p[head], p[head+1]) >= 2*a*w[i]*dn(p[head], p[head+1])) ++head;
dp[i] = dp[p[head]] + a*(w[i]-w[p[head]])*(w[i]-w[p[head]]) + b*(w[i]-w[p[head]]) + c;
while(tail > head && up(p[tail-1], p[tail])*dn(p[tail], i) <= up(p[tail], i)*dn(p[tail-1], p[tail])) --tail;
p[++tail] = i;
dp[0] = 0;
// 因为可以从dp[0]转移,所以要把0添加到队列里去
// 但是这样计算的话dp[0]=c,所以要把dp[0]设为0
} return cout<<dp[n], 0;
}
[ZJOI2007] 仓库建设
当你打完任务安排后就会发现这道题简单的跟1+1似的。
一道简单坑人的小地痞。令 dp[i]
表示在第 \(i\) 个工厂建仓库的最大价格。那么在这个工厂和上一个工厂之间的所有工厂的存货都要运送到仓库 \(i\) 里。那么对于一个在此之间工厂 \(k\),它的送货价值就为 \(p[k]*(x[i]-x[k])=p[k]*x[i]-p[k]*x[k]\),所以我们可以预处理出所有 \(p[k]\) 的前缀和 \(P\) 和所有 \(p[k]*x[k]\) 的前缀和 \(T\)。于是地痞方程为:
\]
调整得:
\]
发现min里的狮子都和 \(j\) 有关,于是提出来:\(dp[j]-T[j]=dp[i]+x[i]*P[j]\)。显然 \(x[i]\) 为斜率。因为求min,所以维护下凸包。
hack 数据中末尾会有一连串 \(p=0\) 的工厂,不会对答案产生任何贡献(他们无需运送货物,也不会被作为仓库),但是 dp 过程中会把他们算进去,因此答案应当取最后一个有货物的工厂作为仓库时的最小花费。—— Ithea686
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
int n, x[N], c[N], t[N], w[N], dp[N], p[N], tail, head = 1, ans = LONG_LONG_MAX;
inline int up(int a, int b){ return dp[b] + t[b] - dp[a] - t[a]; }
inline int dn(int a, int b){ return w[b] - w[a]; }
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n;
for(int i=2; i<=n+1; ++i){
cin>>x[i]>>w[i]>>c[i];
t[i] = t[i-1] + x[i]*w[i];
w[i] += w[i-1];
}
for(int i=1; i<=n+1; ++i){
while(head < tail && up(p[head], p[head+1]) <= x[i]*dn(p[head], p[head+1])) ++head;
dp[i] = dp[p[head]] + x[i]*(w[i-1]-w[p[head]]) - t[i-1] + t[p[head]] + c[i];
while(head < tail && up(p[tail-1], p[tail])*dn(p[tail], i) >= up(p[tail], i)*dn(p[tail-1], p[tail])) --tail;
p[++tail] = i;
}
for(int i=n+1; i>=1; --i){
ans = min(ans, dp[i]);
if(w[i]-w[i-1]) break;
}
return cout<<ans, 0;
}
[学习笔记] 斜率优化DP - DP的更多相关文章
- 学习笔记·斜率优化 [HNOI2008]玩具装箱
\(qwq\)今天\(rqy\)给窝萌这些蒟蒻讲了斜率优化--大概是他掉打窝萌掉打累了吧顺便偷了\(rqy\)讲课用的图 \(Step \ \ 1\) 一点小转化 事实上斜率优化是专门用来处理这样一类 ...
- 「学习笔记」wqs二分/dp凸优化
[学习笔记]wqs二分/DP凸优化 从一个经典问题谈起: 有一个长度为 \(n\) 的序列 \(a\),要求找出恰好 \(k\) 个不相交的连续子序列,使得这 \(k\) 个序列的和最大 \(1 \l ...
- 【学习笔记】动态规划—各种 DP 优化
[学习笔记]动态规划-各种 DP 优化 [大前言] 个人认为贪心,\(dp\) 是最难的,每次遇到题完全不知道该怎么办,看了题解后又瞬间恍然大悟(TAT).这篇文章也是花了我差不多一个月时间才全部完成 ...
- 队列优化和斜率优化的dp
可以用队列优化或斜率优化的dp这一类的问题为 1D/1D一类问题 即状态数是O(n),决策数也是O(n) 单调队列优化 我们来看这样一个问题:一个含有n项的数列(n<=2000000),求出每一 ...
- 汇编入门学习笔记 (七)—— dp,div,dup
疯狂的暑假学习之 汇编入门学习笔记 (七)-- dp.div.dup 參考: <汇编语言> 王爽 第8章 1. bx.si.di.和 bp 8086CPU仅仅有4个寄存器能够用 &qu ...
- 深度学习笔记:优化方法总结(BGD,SGD,Momentum,AdaGrad,RMSProp,Adam)
深度学习笔记:优化方法总结(BGD,SGD,Momentum,AdaGrad,RMSProp,Adam) 深度学习笔记(一):logistic分类 深度学习笔记(二):简单神经网络,后向传播算法及实现 ...
- 算法笔记--斜率优化dp
斜率优化是单调队列优化的推广 用单调队列维护递增的斜率 参考:https://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html 以例1举 ...
- 一类斜率优化的dp(特有性质:只能连续,不能交叉)
hdu3480 给定一个有n个数的集合,将这个集合分成m个子集,要求子集的并等于全集求花费最小. 花费为该子集的(最大数-最小数)的平方. 我们将n个数排序, a < b < c < ...
- [NOI2014]购票 --- 斜率优化 + 树形DP + 数据结构
[NOI2014]购票 题目描述 今年夏天,NOI在SZ市迎来了她30周岁的生日. 来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每 ...
- Luogu P5468 [NOI2019]回家路线 (斜率优化、DP)
题目链接: (luogu) https://www.luogu.org/problemnew/show/P5468 题解: 爆long long毁一生 我太菜了,这题这么简单考场上居然没想到正解-- ...
随机推荐
- 解决NodeJS Downloading node-sass 卡死慢安装失败的问题
之前写过一篇从0开始的NodeJS安装配置教程,在那篇文章结尾提到使用过程中还有一个坑,只是没有遇到就没写,时隔多日在我使用某开源项目的时候又遇到了这个问题 下载依赖时一直卡在 Downloading ...
- Windows10在WSL中运行GUI应用
0. 首先在WSL装X11相关环境 需要安装x11和桌面环境, 在这里装的是xfce4 sudo apt install x11-apps sudo apt install xfce4 有两种显示 ...
- Linux 内核:设备树 学习总结
背景 之前写过设备树DTS 学习:学习总结(应用篇)的学习,但是是偏向于应用:这次针对了设备树的架构以及在驱动中的使用流程做了补充. 基于 Linux 内核 v4.14. 目录 标题 说明 设备树:d ...
- 海思SDK 学习 :000-海思HI35xx平台软件开发快速入门之背景知识
背景 参考自:<HiMPP V3.0 媒体处理软件开发参考.pdf> 由于在音视频处理领域,海思芯片占有全球市场的很大份额.当我们选择使用海思芯片开发时,程序开发模型主要是围绕HIMPP( ...
- Node.js - fs.path模块
首先我有话说,是谁说的学完ajax就可以去vue了,太天真了我,学会js钻出来个ajax,学完ajax钻出来个node.js这一步步的,当然node不会学到太深入把表面的认识一下就可以了,这之后可能更 ...
- 很好用的SSH工具FinalShell
上图片:1.远程连接Linux 2.Linux:CentOS 3.虚拟机:
- 【算法】用c#实现计算方法中的经典降幂优化策略,减少计算复杂度
对于给定的数组[x1,x2,x3,-,xn],计算幂的累积:x1^(x2^(x3^(-^xn))的最后一位(十进制)数字. 例如,对于数组[3,4,2],您的代码应该返回1,因为3^(4^2)=3^1 ...
- oeasy教您玩转python - 9 - # 换行字符
换行字符 回忆上次内容 数制可以转化 bin(n)可以把数字转化为 2进制 hex(n)可以把数字转化为 16进制 int(n)可以把数字转化为 10进制 编码和解码可以转化 encode 编码 ...
- 网络基础 CAS协议学习总结
架构介绍 系统组件 CAS服务器和客户端构成了CAS系统体系结构的两个物理组件,它们通过各种协议进行通信. CAS服务器 CAS服务器是基于Spring Framework构建的Java servle ...
- [ABC363G] Dynamic Scheduling 与 P4511 [CTSC2015] 日程管理
思路: 对于插入操作,设插入 \(\{t,p\}\): 若当前 \(1 \sim t\) 有空位,那么就放进去. 否则,\(1 \sim t\) 是被塞满了的: 首先容易想到的是找到 \(1 \sim ...