CodeForces - 319C Kalila and Dimna in the Logging Industry

本文探讨了一种基于动态规划和斜率优化的算法,用于解决寻找最优砍树序列的问题。通过定义状态f[i]为砍到第i棵树的最小代价,利用转移方程进行状态更新,最终求得砍完所有树的最小代价。文章提供了详细的算法思路和C++代码实现。

Solution

我们令 f[i]f[i]f[i] 为砍第 iii 棵的最小代价。

有转移:

f[i]=f[j]+a[i]×b[j](1≤j≤i−1)f[i]=f[j]+a[i]\times b[j](1\le j \le i-1)f[i]=f[j]+a[i]×b[j](1ji1)

我们考虑为什么这是对的。(因为实际上我们可以选择先砍后面的再砍前面的)

首先我们证明不存在先砍 kkk,再砍 jjj 的情况(其中 k≠n,j<kk\ne n,j<kk=n,j<k)。因为 b[n]=0b[n]=0b[n]=0,所以先砍了 kkk,直接往后砍(因为 b[k]<b[j]b[k]<b[j]b[k]<b[j]),等砍到 nnn(即花费为 000)时再回来一定更优。

所以用斜率优化求出 f[n]f[n]f[n] 就行了。

Code

#include <cstdio>

#define rep(i, _l, _r) for(register signed i = (_l), _end = (_r); i <= _end; ++ i)
#define fep(i, _l, _r) for(register signed i = (_l), _end = (_r); i >= _end; -- i)
#define erep(i, u) for(signed i = head[u], v = to[i]; i; i = nxt[i], v = to[i])
#define print(x, y) write(x), putchar(y)

template <class T> inline T read(const T sample) {
	T x = 0; int f = 1; char s;
	while((s = getchar()) > '9' || s < '0') if(s == '-') f = -1;
	while(s >= '0' && s <= '9') x = (x << 1) + (x << 3) + (s ^ 48), s = getchar();
	return x * f;
}
template <class T> inline void write(const T x) {
	if(x < 0) return (void) (putchar('-'), write(-x));
	if(x > 9) write(x / 10);
	putchar(x % 10 ^ 48);
}
template <class T> inline T Max(const T x, const T y) {return x > y ? x : y;}
template <class T> inline T Min(const T x, const T y) {return x < y ? x : y;}
template <class T> inline T fab(const T x) {return x > 0 ? x : -x;}
template <class T> inline T Gcd(const T x, const T y) {return y ? Gcd(y, x % y) : x;}

typedef long long ll;

const int N = 1e5 + 5;

int n, a[N], b[N], q[N], head = 1, tail;
ll f[N];

double slope(const int j, const int k) {return 1.0 * (f[k] - f[j]) / (b[j] - b[k]);}

int main() {
	n = read(9);
	rep(i, 1, n) a[i] = read(9);
	rep(i, 1, n) b[i] = read(9);
	rep(i, 1, n) {
		while(head < tail && slope(q[head], q[head + 1]) < a[i]) ++ head;
		f[i] = f[q[head]] + 1ll * a[i] * b[q[head]];
		while(head < tail && slope(q[tail], i) < slope(q[tail - 1], q[tail])) -- tail;
		q[++ tail] = i;
	}
	print(f[n], '\n');
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值