博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
斜率优化DP学习笔记
阅读量:4479 次
发布时间:2019-06-08

本文共 2965 字,大约阅读时间需要 9 分钟。

最近瞎搞了搞斜率优化...

斜率优化DP是一种优化DP的思想,通过高中数学线性规划那套理论加上凸包那套理论单调的优良性质将O(N^2)的DP枚举转移优化成O(N)的转移

先来一道简单题:[APIO2010]特别行动队

我们设\(s_i\)是前缀和数组,设\(f_i\)为前\(i\)个巨佬的战斗力和最大值,不难列出状态转移方程:

\[f_i=\max(f_j+a(s_i-s_j)^2+b(s_i-s_j)+c)\]

把式子拆开得到

\[f_i=f_j+as_i^2-2as_is_j+as_j^2+bs_i-bs_j+c\]

移项、合并项

\[as_j^2+f_j=(2as_i+b)s_j+(f_i-as_i^2-bs_i-c)\]

如果我们将\((s_j,as_j^2+f_j)\)当做笛卡尔平面上的点,那么\(f_i\)的最大值就是以\(2as_i+b\)为斜率、经过这些点截距的最大值加上\(as_i^2+bs_i+c\)

根据题意,\(s_j\)是单调递增的,\(2as_i+b\)是单调递减的,所以我们可以维护上凸壳,根据转移中斜率的单调性来在凸壳上确定转移点。由于是上凸壳,并且斜率单调递减,所以转移点是单调递增的,即我们可以单调地维护赚一点。转移后再将新的点加入上凸壳即可。时间复杂度为\(O(n)\)。由于坐标都是整点,需要通过叉积判凸壳,一般都要开long long,这里偷懒直接define int long long下面是乱七八糟的代码/

#include 
#define int long longusing namespace std;int n, a, b, c, s[1000010];int f[1000010];int x[1000010], y[1000010], p, tot;signed main(){ scanf("%lld", &n); scanf("%lld%lld%lld", &a, &b, &c); for (int i = 1; i <= n; i++) scanf("%lld", &s[i]), s[i] += s[i - 1]; tot = 1, p = 1; for (int i = 1; i <= n; i++) { int k = 2 * a * s[i] + b; while (p < tot && y[p + 1] - k * x[p + 1] >= y[p] - k * x[p]) p++; f[i] = y[p] - k * x[p] + a * s[i] * s[i] + b * s[i] + c; int newx = s[i], newy = a * s[i] * s[i] + f[i]; while (tot > 1 && (newx - x[tot]) * (y[tot] - y[tot - 1]) - (newy - y[tot]) * (x[tot] - x[tot - 1]) <= 0) tot--; if (p > tot) p = tot; x[++tot] = newx; y[tot] = newy; } printf("%lld\n", f[n]); return 0;}

练习:玩具装箱toy

再来看一道题:

假设土地A的面积可以被土地B的面积完全包含,由于需要购买所有土地,所以我们可以只购买B(A跟着一起购买了)。我们把每个土地面积当做二维平面上的整点(x,y),则对于所有存在偏序关系的点都可以被删除。我们可以按x排序,第二维通过单调栈来清除所有存在偏序关系的点,这样就剩下了满足x单增、y单减的点。

FJ每次选土地一定是选一段连续的区间更优:假设某一段区间内有土地不选,则显然该土地在这段区间内选择不会比不在区间内选择差,所以我们还是要选择连续的土地一起买。

我们可以设状态了,设\(f_i\)为购买前\(i\)块土地最小值,则

\[f_i=\min(f_j+y[j+1]*x[i])\]

显然我们可以通过斜率优化,稍微推一下式子即可,这里就不啰嗦了,下面是乱七八糟的代码

#include 
#define int long longusing namespace std;struct fuck{ int x, y;} a[50010];int n, tot, f[50010];int x[50010], y[50010];signed main(){ scanf("%lld", &tot); for (int i = 1; i <= tot; i++) scanf("%lld%lld", &a[i].x, &a[i].y); sort(a + 1, a + 1 + tot, [](const fuck &a, const fuck &b) {return a.x < b.x;}); for (int i = 1; i <= tot; i++) { while (n > 0 && a[n].y <= a[i].y) n--; a[++n] = a[i]; } tot = 1; x[1] = a[1].y, y[1] = 0; int p = 1; for (int i = 1; i <= n; i++) { if (p > tot) p = tot; while (p < tot && a[i].x * x[p + 1] + y[p + 1] < a[i].x * x[p] + y[p]) p++; f[i] = a[i].x * x[p] + y[p]; while (tot > 1 && (x[tot] - x[tot - 1]) * (f[i] - y[tot]) - (y[tot] - y[tot - 1]) * (a[i + 1].y - x[tot]) > 0) tot--; y[++tot] = f[i]; x[tot] = a[i + 1].y; } printf("%lld\n", f[n]); return 0;}

时间复杂度\(O(n\log n)\)(因为有个排序),注意DP过程的时间复杂度是\(O(n)\)的。

斜率优化DP最主要就是先写暴力,然后推式子,推出类似一次函数解析式形式,然后稍微分析下凸包情况即可

注意开long long,有必要可以开double。

转载于:https://www.cnblogs.com/oier/p/10227129.html

你可能感兴趣的文章
vue3.0学习笔记(一)
查看>>
jQuery跨域
查看>>
MySQL的explain中的参数说明
查看>>
JAVA基本数据类型、引用数据类型-参数传递详解
查看>>
sun.misc.Unsafe 详解
查看>>
食堂排队问题的一个实现
查看>>
Git 回滚代码的正确姿势
查看>>
构造函数、析构函数、虚析构函数、纯虚析构函数要点
查看>>
【原创】学习日记2:nginx配置文件2012.01.06
查看>>
nginx知识图谱
查看>>
[转载]Bison-Flex 笔记
查看>>
[转]Android的Handler总结
查看>>
初始化一个新的服务器
查看>>
ServletConfig
查看>>
顺序栈用C语言实现
查看>>
Python批量获取京东商品列表信息
查看>>
2017.7.10 C组总结
查看>>
SourceTree下载 及使用
查看>>
MyEclipse下安装FatJar打包工具
查看>>
什么是域名-视频讲解?
查看>>