[学习笔记] 斜率优化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毁一生 我太菜了,这题这么简单考场上居然没想到正解-- ...
随机推荐
- MySQL Docker搭建挂载并启用远程连接
1.拉取镜像 后面可以指定版本号,这里使用8.0 docker pull docker.io/mysql:8.0 2.查看mysql镜像 docker images 3.启动docker并挂载 doc ...
- 交通规划四阶段法:基于 Python 的交通分布预测算法复现 - 附完整代码链接
目录 交通规划四阶段法:基于 Python 的交通分布预测算法复现 - 附完整代码链接 我只是想使用这些代码 下载代码文件 代码的使用方法 合作 部分代码内容的展示 交通规划四阶段法:基于 Pytho ...
- spring多数据源配置笔记
本文阐述使用多数据源的额场景,以及如何使用springboot的配置多数据源. 关于后者,主要是直接引用其它博文:https://blog.csdn.net/u012060033/article/de ...
- CF1320D Reachable Strings
110和011互相转化,相当于就是0在连续两个1的情况下,移动两个位置 能够发现,0的位置的奇偶不会改变,且很多个0之间的相对位置不会改变 猜想考虑这个答案只跟0的奇偶性有关,下面小证一下:(注意下面 ...
- 让matplotlib在绘图时显示中文
让matplotlib绘图时显示中文. 安装中文字体 apt install fonts-wqy-microhei 清除matplotlib的缓存 rm -rf ~/.cache/matplot ...
- SQL注入攻击及防御
SQL注入攻击及防御 1.项目实验环境 目标靶机OWASP_Broken_Web_App_VM_1.2: https://sourceforge.net/projects/owaspbwa/files ...
- win10系统常用命令(netstat、ping、telnet、sc、netsh命令)
netstat命令 1. 查找端口占用 netstat -ano netstat -ano | findstr 5000 ping命令 ping 192.168.1.1 ping baidu.com ...
- Unity中自定义应用程序打开Assets目录下指定类型的文件
在Unity使用VS2017打开unityShader文件时总提示错误: 我也一直没找啥原因,shader文件直接使用VSCode打开,当然其他类型的文件也可这样处理用相应的exe打开,如:pdf,t ...
- java中的基准测试框架JMH
JHM是openJDK开发的一个benchmark框架.它是一个Maven依赖,所以创建一个Maven项目,引入下面两个依赖: <dependency> <groupId>or ...
- Uipath学习(一)
Uipath 常用程序包总结: (1)正则表达式依赖程序包: System.Text.RegularExpressions 用法: 这个语句的作用是判断"溢达集团"这一串字符是否为 ...