动态规划——斜率优化

斜率优化问题

print article

容易想到用dp[i]来表示打印前i个单词的最小花费
先预处理一下前缀和sum[i] = sum[i-1] + c[i] 后面用起来比较方便

转移方程为:
dp[i] = dp[j] + (sum[i] - sum[j])^2 + m (j < i)
但是这样比较麻烦,复杂度为O(n^2) 会超时
事实上有很多点根本用不上, 与后面点比起来任何时候都没有优势,这样的点我们应该及时去掉,避免重复运算

不妨设 k < j
若dp[j] + (sum[i] - sum[j]) ^ 2 + m <= dp[k] + (sum[i] - sum[k]) ^ 2 + m
则j优势于k, k则可以去掉, k永世不得翻身

化简上式可得
((dp[j] + sum[j] * sum[j]) - (dp[k] + sum[k] * sum[k])) / (sum[j] - sum[k]) <= 2*sum[i]
不等式左边可以看做一个斜率, 右边为一个递增的数 所以说j优势于k 且k永世不翻身

我们可以把上式独立为两个函数 getup(j, k) / getdown(j, k) <= 2 * sum[i]

记judge(i, j) = getup(i, j) / getdown(i, j)

对于末尾元素ed 如果有 judge(ed-1, ed) >= judge(ed, i) 则 ed需要舍去
为什么呢?
不妨设 k < j < i
如果jugde(j, k) > sum[i] 则k优势于j
如果judge(j, k) <= sum[i] 则j优势于k 但是还有judge(j, i) <= sum[i] 则i优势于j
综上j是无意义的 所以我们维护的是一个斜率单调递增的队列

这样的话我们就可以用一个单调队列deque来维护,队首为最优选择 当然用数组也可

#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <cstdio>
#define ll long long
using namespace std;
const int MAXN = 5e5 + 7;
ll sum[MAXN];
ll a[MAXN];
ll dp[MAXN];
deque<int> q;
int n, m;
ll getup(int a, int b)
{
	return (dp[a] + sum[a] * sum[a]) - (dp[b] + sum[b] * sum[b]);
}
ll getdown(int a, int b)
{
	return (sum[a] - sum[b]);
}
int main()
{
	while (cin >> n >> m)
	{
		for(int i = 1; i <= n; i++)
		{
			scanf("%lld", &a[i]);
			sum[i] = sum[i-1] + a[i];
		}
		q.clear();
		q.push_back(0); // 初始化斜率为0 很重要
		for(int i = 1; i <= n; i++)
		{
			while(q.size() > 1 && getup(q[1], q.front()) <= 2 * sum[i] * getdown(q[1], q.front())) 
				q.pop_front(); // 维护单增队首

			dp[i] = dp[q.front()] + getdown(i, q.front()) * getdown(i, q.front()) + m;

			while(q.size() > 1 && 
				getup(q.back(), q[q.size()-2]) * getdown(i, q.back()) 
				>= getup(i, q.back()) * getdown(q.back(), q[q.size() - 2])) // 维护斜率单增队尾
				q.pop_back();
				q.push_back(i);
		}	
		cout << dp[n] << endl;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值