题目链接

刚学斜率优化,写个总结qaq

题目大意:转移方程\(f[i] = min\{f[j]+(sum[i]-sum[j]+i-j-1-L)^2\}\),求\(f[n]\)

斜率优化


分析:首先我们肯定不能\(O(n^2)\)\(dp\),我们来拆一波式子,先把\(min\)符号去掉,然后令\(L=L+1\)

\(f[i] = f[j]+(sum[i]-sum[j]+i-j-1-L)^2\)

\(f[i] = f[j]+(sum[i]-sum[j]+i-j-L)^2\)

考虑换元,用\(s[i]\)代表\(sum[i]+i\)

\(f[i]=f[j]+(s[i]-s[j]-L)^2\)

完全平方公式

\(f[i]=f[j]+s[i]^2-2\times s[i] \times (s[j]+L)+(s[j]+L)^2\)

然后我们发现有的和\(i\)有关,有的和\(j\)有关,有的和两个都有关,我们考虑将其分类:

\(i\)有关的\(f[i],s[i]^2\)

\(j\)有关的\(f[j],(s[j]+L)^2\)

都有关的:\(-2\times s[i] \times (s[j]+L)\)

我们考虑将其改写为\(y=kx+b\)的形式,将和\(i\)有关的作为\(b\),将和\(i,j\)都有关的作为\(k,x\),由于是乘积,我们将其中有\(i\)的项作为\(k\),和\(j\)有关的作为\(x\),然后将只和\(j\)有关的作为\(y\)

所以

\(y=f[j]+(s[j]+L)^2\)

\(k=2\times s[i]\)

\(x=s[j]+L\)

\(b=f[i]-s[i]^2\)

建立平面直角坐标系,以上文的\(x,y\)把决策点放进坐标系。然后我们发现问题变成了,一个坐标系内有若干个点,一个斜率不变的直线经过任意一个点,使得截距最小化

我们往坐标系内加入决策点时,\(x\)坐标是递增的,一个显而易见的结论:有用的决策点的集合,过相邻点直线的斜率一定单调递增

于是我们可以用单调队列来维护这个下凸壳使得斜率单调递增。

那么我们如何这些决策点集合里面取最优呢,因为\(k\)是单调递增的,所以每次做决策时的直线斜率单调递增,一个点能成为决策点当且仅当左边直线的斜率比\(k\)小,右边直线的斜率比\(k\)大,又因为斜率单调递增,我们也可以用单调队列来维护,不断弹队头即可

因为每个点只会进队/出队一次,所以时间复杂度是\(O(n)\)

#include <iostream>
using namespace std;
const int maxn = 5e4 + 100;
typedef long long ll;
int n,L,head,tail,q[maxn];
ll sum[maxn],f[maxn];
template <typename T>
inline T square(const T &x){return x * x;}
inline double s(int i){return sum[i] + i;}
inline double k(int i){return 2 * s(i);}
inline double y(int i){return f[i] + square(s(i) + L);}
inline double x(int i){return s(i) + L;}
inline double b(int i){return f[i] - square(s(i));}
inline double slope(int i,int j){return (y(j) - y(i)) / (x(j) - x(i));}
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> L;L++;
	for(int i = 1;i <= n;i++)
		cin >> sum[i],sum[i] += sum[i - 1];
	head = tail = 1;
	for(int i = 1;i <= n;i++){
		while(head < tail && slope(q[head],q[head + 1]) < k(i))head++;
		f[i] = f[q[head]] + square(s(i) - s(q[head]) - L);
		while(head < tail && slope(i,q[tail - 1]) < slope(q[tail - 1],q[tail]))tail--;
		q[++tail] = i; 
	}	
	cout << f[n] << '\n';
	return 0;
}

相关文章:

  • 2021-07-01
  • 2021-12-03
  • 2021-11-20
猜你喜欢
  • 2021-09-15
  • 2021-11-04
  • 2022-01-19
  • 2021-06-05
  • 2021-11-21
  • 2021-06-14
  • 2021-04-08
相关资源
相似解决方案