仓库建设 HYSBZ - 1096 dp 斜率优化 前缀和

题解

dp求解,f[i]表示i点作为仓库前面i个点合理分配的最小代价,每次从前面作为仓库的点j进行转移,加上区间[j, i)移动到i的代价。
区间[j, i)移动到i的代价使用前缀和求解。s[i] = ∑(j=1->i){ x[j] * p[j] },sp[i] = ∑(j=1->i){ p[j] }
cot[j, i] = ∑(k=j+1->i){ p[k] * (x[i] - x[k]) } = ∑(k=j+1->i){ p[k] } * x[i] - ∑(k=j+1->i){ p[k] * x[k] }
转移方程 f[i] = min(f[i], c[i] + f[j] + (sp[i] - sp[j]) * x[i] - (s[i] - s[j]));
斜率方程(k < j < i) ((f[j] + s[j]) - (f[k] + s[k])) / (sp[j] - sp[k]) < x[i]

在点i从j转移比k优则在点t从j转移也比k优,证明略。。

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 = 1e6 + 10;
ll x[N], p[N], c[N];
ll s[N], sp[N], ls[N], head, tail; //x[i] * p[i]前缀和   p[i]前缀和
ll f[N]; //f[i]表示i点为仓库前i个点合理分配的最小代价
/*
将(j, i)内货物移动到i点的代价
cot[j, i]=∑(k=j+1->i){ p[k] * (x[i] - x[k]) } = ∑(k=j+1->i){ p[k] } * x[i] - sigma(k=j+1->i){ p[k] * x[k] }
= (sp[i] - sp[j]) * x[i] - (s[i] - s[j]) 
转移方程
f[i] = min(f[i], c[i] + f[j] + (sp[i] - sp[j]) * x[i] - (s[i] - s[j]));
斜率方程(k < j < i)
((f[j] + s[j]) - (f[k] + s[k])) / (sp[j] - sp[k]) < x[i]
*/
inline ll up(int k, int j)
{
	return f[j] + s[j] - f[k] - s[k];
}
inline ll down(int k, int j)
{
	return sp[j] - sp[k];
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i)
	{
		scanf("%lld%lld%lld", &x[i], &p[i], &c[i]);
		s[i] = s[i - 1] + x[i] *p[i];
		sp[i] = sp[i - 1] + p[i];
	}
	memset(f, 0x3f, sizeof(f));
	ls[tail++] = 0; //从0号点转移
	f[0] = 0;
	for (int i = 1; i <= n; ++i)
	{
		while (head + 1 < tail && up(ls[head], ls[head + 1]) <= x[i] * down(ls[head], ls[head + 1]))
			++head;
		int j = ls[head];
		f[i] = c[i] + f[j] + (sp[i] - sp[j]) * x[i] - (s[i] - s[j]);
		while (head + 1 < tail && up(ls[tail - 2], ls[tail - 1]) * down(ls[tail - 1], i) >= up(ls[tail - 1], i) * down(ls[tail - 2], ls[tail - 1]))
			--tail;
		ls[tail++] = i;
	}
	cout << f[n] << endl;

	return 0;
}

猜你喜欢

转载自blog.csdn.net/CaprYang/article/details/88354721