斜率优化(DP)

Posted 。✧* ꧁王者꧂✧*

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了斜率优化(DP)相关的知识,希望对你有一定的参考价值。

Saber!!!
先来个华丽的出场~~
今天,蒟蒻仍在赶知识点的路上。昨天刚学过数位DP,今天又学了斜率优化,被爆锤了。
不扯淡了,现在就来写一下总结吧。
斜率优化,既然叫这个名字,那么,这个算法的性质其实就已经确定了:DP优化。
也就是说,它本身并不像树形DP或数位DP那样解决一些特定性质的问题的,而是类似于矩阵乘法那样,对于某些DP方程,加速地进行转移,以用来攻克一些给出的n较大时的情况。
学了斜率DP,你会发现,自己学了多年的数学终于!!!在此刻派上了用场。斜率DP,顾名思义,与平面几何,也就是我们所学的一次函数有关。斜率优化往往是用于一些具有递推性质的DP方程中去的。
如:
在这里插入图片描述
本题就是一道典型的斜率优化问题。首先,在写斜率优化题目的时候,最重要的一步并非优化,而是想出DP转移方程。优化可以没有,但没有转移方程,一定不行。
就如本题,若n的范围够小的话,我们可以双重循环枚举来搞。开一个f数组, f [ i ] f[i] f[i]表示将前 i i i个任务分组后的最小总费用。求 f [ i ] f[i] f[i],我们枚举 j ( 1 − ( i − 1 ) ) j( 1-(i-1) ) j(1(i1)),将 j − i j-i ji分为一组,求最小总费用。
但是,很显然,本题的 n n n太大了, O ( n 2 ) O(n^2) O(n2)肯定过不去,这时,就要考虑斜率优化了。
考虑一下 f [ i ] f[i] f[i]的表达式: f [ i ] = f [ j ] + ( s u m c [ i ] − s u m c [ j ] ) ∗ s u m t [ i ] + s ∗ ( s u m c [ n ] − s u m c [ j ] ) 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]),将其展开,化为 y = k x + b y=kx+b y=kx+b的形式: f [ j ] = ( s u m t [ i ] + s ) ∗ s u m c [ j ] + f [ i ] − s u m c [ i ] ∗ s u m t [ i ] − s ∗ s u m c [ n ] f[j]=(sumt[i]+s)*sumc[j]+f[i]-sumc[i]*sumt[i]-s*sumc[n] f[j]=sumt[i]+ssumc[j]+f[i]sumc[i]sumt[i]ssumc[n],如式子,我们将 s u m c [ j ] sumc[j] sumc[j]视为一次函数中的 x x x ( s u m t [ i ] + s u m c [ j ] ) (sumt[i]+sumc[j]) sumt[i]+sumc[j]视为 k k k f [ j ] f[j] f[j]视为 y y y f [ i ] − s u m c [ i ] ∗ s u m t [ i ] − s ∗ s u m c [ n ] f[i]-sumc[i]*sumt[i]-s*sumc[n] f[i]sumc[i]sumt[i]ssumc[n]视为 b b b,那么,斜率相同的情况下,为使 f [ i ] f[i] f[i] m i n min min,我们就要在 ( s u m c [ j ] , f [ j ] ) (sumc[j],f[j]) (sumc[j],f[j])的点集中找到使这条直线从下向上找,找到的第一个点便是所需的 j j j
如图:
在这里插入图片描述

对于任一条斜率大于0的直线,先找到的一定都是图中的粉色的点,而这三点则恰好构成了本图的一个凸包,所以,我们在找点时,实际上只需维护凸包即可,又因本题的 s u m c [ i ] ( 也 就 是 一 次 函 数 的 k ) sumc[i](也就是一次函数的k) sumc[i]k是单调递增的,所以,我们可以将凸包中斜率小于当前的 s u m c [ i ] sumc[i] sumc[i]的点删去,这就是本题的大致思路了。
代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=4e5;
typedef long long LL;
int l=0,r=0,n,s,q[N];
LL f[N],c[N],t[N];
int main()
{
	cin>>n>>s;
	for(int i=1;i<=n;i++)
	{
		int tt,cc;
		scanf("%d%d",&tt,&cc);
		t[i]=t[i-1]+tt;
		c[i]=c[i-1]+cc;
	}
	for(int i=1;i<=n;i++)
	{
		while(l<r&&(f[q[l+1]]-f[q[l]]<(s+t[i])*(c[q[l+1]]-c[q[l]]))) l++;
		f[i]=f[q[l]]+t[i]*(c[i]-c[q[l]])+s*(c[n]-c[q[l]]);
		while(l<r&&((f[i]-f[q[r]])*(c[q[r]]-c[q[r-1]])<=(f[q[r]]-f[q[r-1]])*(c[i]-c[q[r]]))) r--;
		q[++r]=i;
	}
	cout<<f[n];
	return 0;
}

好了,本次的博客就更新到这里。

以上是关于斜率优化(DP)的主要内容,如果未能解决你的问题,请参考以下文章

斜率优化DP

hdu 1300 Pearls(线性dp/斜率优化)

斜率优化DP

模板斜率优化dp

斜率优化DP和四边形不等式优化DP整理

『土地征用 Land Acquisition 斜率优化DP』