斜率优化DP

对于一个有i,j,ij项的转移式,我们不能用单调队列去优化

我们可以把一个转移方程式化成一个一次函数解析式:

b代表只有i的项

y代表只有j的项

x代表有i且有j的项中含j的项

k代表有i且有j的项中含i的项

所以我们要求的就是b,也就是截距,这样我们可以用斜率维护一个凸包,每次把队头(也可以是栈,或者是二分)取出来计算答案(根据最开始的解析式),更新队尾(根据斜率)维护凸包

P3195玩具装箱

首先可以推出:f[i]=min(f[j]+(sun[i]-sum[j]-j+i-L-1)^2 (sum为输入的前缀和)

然后化:

设A[i]=sum[i]+i

B[i]=sum[i]+i+L+1

∴f[i]=f[j]+(a[i]-b[j])^2

f[i]=f[j]+a[i]^2+b[j]^2+2*a[i]*b[j]

(f[j]+b[j])=(2*a[i])*(b[j])+(f[i]+a[i]^2)

即y=kx+b

因为我们要求dp[i]最小,所以我们需要求一个斜率最小的线

​
#include<iostream>
using namespace std;
long double f[51419], sum[51419];int  n, l;
double B(int j) { return sum[j] + j + 1 + l; }
double A(int i) { return sum[i] + i; }
double k(int i) { return 2 * A(i); }
double y(int j) { return f[j] + B(j)*B(j); }
double x(int j) { return B(j); }
double slope(int i, int j) { return (y(i) - y(j)) / (x(i) - x(j)); }
int q[51419];
int main() {
	cin >> n >> l;
	for (int i = 1; i <= n; i++) {
		int a;
		cin >> a;
		sum[i] = (sum[i - 1] + a);
	}
	int l, r;
	l = r = 1;
	for (int i = 1; i <= n; i++) {
		while (l < r && slope(q[l], q[l + 1]) < 2 * A(i)) ++l;//看看可不可以找到斜率比现在这根线小的
		f[i] = f[q[l]] + (A(i) - B(q[l])) * (A(i) - B(q[l]));
		while (l < r && slope(i, q[r - 1]) < slope(q[r - 1], q[r])) --r;//更新队列
		q[++r] = i;
	}
	cout << long long(f[n]);
}

​

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我爱游戏啊啊啊啊啊啊

爸爸您的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值