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

本文详细解析了使用动态规划解决仓库选址问题的方法,通过计算最小代价来确定最优仓库位置。介绍了解题思路,包括状态定义、转移方程及斜率方程的应用,提供了一份AC代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值