玩具装箱toy HYSBZ - 1010 dp 斜率优化

题解

使用dp求解,转移方程f[i] = min(f[i], f[j] + (s[i] - s[j] - l)^2),复杂度O(N^2),使用斜率优化。
j1 < j2 < i 在 j2比j1好的前提下 斜率方程
f[j2] + (s[i] - s[j2] - l)^2 < f[j1] + (s[i] - s[j1] - l)^2 将j放在左边i和常量放在右边
((f[j2] + s[j2]^2) - (f[j1] + s[j1]^2)) / (s[j2] - s[j1]) < 2(s[i]-L)
令x=(f[j] + s[j]^2), y=s[j]可以表示为点(y, x)计算斜率

当前位置j1没j2优则对于后面转移也不会用j1,可以直接将j1淘汰。
证: j1 < j2 < i < i2 令s[i2] = s[i] + v
f[j2] + (s[i] + v - s[j2] - l)^2 < f[j1] + (s[i] + v - s[j1] - l)^2 利用原式化为
-2vs[j2] < -2vs[j1] 由于前缀和非递减的证

AC代码

#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 5e4 + 10;
ll f[N], s[N]; //f[i]前i项的最小代价 s[i]前i项的前缀和+i
int ls[N], head, tail; //维护斜率递增上凸壳
//f[i] = min(f[i], f[j] + (s[i] - s[j] - l)^2)
//j1 < j2 < i 在 j2比j1好的前提下 斜率方程
//f[j2] + (s[i] - s[j2] - l)^2 < f[j1] + (s[i] - s[j1] - l)^2 将j放在左边i和常量放在右边
//((f[j2] + s[j2]^2) - (f[j1] + s[j1]^2)) / (s[j2] - s[j1])  <  2(s[i]-L)
//令x=(f[j] + s[j]^2), y=s[j]可以表示为点(y, x)计算斜率
//j1 < j2 < i < i2 令s[i2] = s[i] + v
//f[j2] + (s[i] + v - s[j2] - l)^2 < f[j1] + (s[i] + v - s[j1] - l)^2 利用原式化为
//-2v*s[j2] < -2v*s[j1] 由于前缀和非递减则说明当前位置j1没j2优则对于后面转移也不会用j1
//可以直接将j1淘汰 如果不满足则需要维护凸壳二分查找
double Y(int j)
{
	return f[j] + 1.0 * s[j] * s[j];
}
double X(int j)
{
	return s[j];
}
double slope(int j1, int j2) //计算两点斜率
{
	//return (f[j2] + 1.0 * s[j2] * s[j2] - f[j1] - 1.0 * s[j1] * s[j1]) / (s[j2] - s[j1]);
	return (Y(j2) - Y(j1)) / (X(j2) - X(j1));
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int n, l;
	cin >> n >> l;
	++l; //方便计算
	for (int i = 1; i <= n; ++i)
		scanf("%lld", &s[i]), s[i] += s[i - 1] + 1;
	ls[tail++] = 0; //可以通过0转移
	for (int i = 1; i <= n; ++i)
	{
		while (head + 1 < tail && slope(ls[head], ls[head + 1]) <= 2 * (s[i] - l)) //队列有两个元素 队首没第二个优
			head++; //淘汰表头
		int j = ls[head]; //通过表头转移
		f[i] = f[j] + (s[i] - s[j] - l) * (s[i] - s[j] - l);
		while (head + 1 < tail && slope(ls[tail - 2], ls[tail - 1]) >= slope(ls[tail - 1], i)) //插入后不满足斜率递增
			tail--; //淘汰表尾
		ls[tail++] = i; //插入当前节点
	}
	cout << f[n] << endl;

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值