【ybt金牌导航1-5-1】【ybt金牌导航1-5-2】【luogu P2365】任务安排1 / 任务安排2 / 任务安排

任务安排1 / 任务安排2 / 任务安排

题目链接:ybt金牌导航1-5-1 / ybt金牌导航1-5-2 / luogu P2365

题目大意

有一些任务,你可以把它分成连续的几段来完成。
对于每一段,要先让机器预热一个固定的时间,然后再完成。
下一段任务要在前面段的任务都完成了才可以开始热机。
这一批任务完成的时间都是他们之间的和,然后每个任务的费用是它完成的时刻乘它的费用系数。
让你找一种方案使得总费用最小,输出总费用。

思路

首先我们可以看出它直接做可以用 DP 来做。
我们用 f i f_i fi 表示费用系数前缀和, t i t_i ti 表示时间前缀和,那我们可以列出方程。
d p i = m i n j = 1 i − 1 { d p j + ( f i − f j ) × t i + S × ( f n − f j ) } dp_i=min_{j=1}^{i-1}\{dp_j+(f_i-f_j)\times t_i+S\times(f_n-f_j)\} dpi=minj=1i1{dpj+(fifj)×ti+S×(fnfj)}

(如果是ybt上的任务安排1就直接用这个方程DP就好了, n 2 n^2 n2 能过的)

然后在这里似乎还是很迷茫,我们拆括号:
d p i = m i n j = 1 i − 1 { d p j + f i × t i − f j × t i + S × f n − S × f j } dp_i=min_{j=1}^{i-1}\{dp_j+f_i\times t_i-f_j\times t_i+S\times f_n-S\times f_j\} dpi=minj=1i1{dpj+fi×tifj×ti+S×fnS×fj}
d p i = m i n j = 1 i − 1 { d p j + f i × t i + S × f n − t i × f j − S × f j } dp_i=min_{j=1}^{i-1}\{dp_j+f_i\times t_i+S\times f_n-t_i\times f_j-S\times f_j\} dpi=minj=1i1{dpj+fi×ti+S×fnti×fjS×fj}
我们会发现 f i × t i + S × f n f_i\times t_i+S\times f_n fi×ti+S×fn 在枚举 i i i 就确定了,剩下的一看,有关 j j j 的就是 d p j , f j dp_j,f_j dpj,fj,发现它可以用斜率优化来做:

对于 a>b,如果 a 比 b 优:
d p a + f i × t i + S × f n − t i × f a − S × f a ≤ d p b + f i × t i + S × f n − t i × f b − S × f b dp_a+f_i\times t_i+S\times f_n-t_i\times f_a-S\times f_a\leq dp_b+f_i\times t_i+S\times f_n-t_i\times f_b-S\times f_b dpa+fi×ti+S×fnti×faS×fadpb+fi×ti+S×fnti×fbS×fb
d p a − t i × f a − S × f a ≤ d p b − t i × f b − S × f b dp_a-t_i\times f_a-S\times f_a\leq dp_b-t_i\times f_b-S\times f_b dpati×faS×fadpbti×fbS×fb
d p a − d p b ≤ t i × f a + S × f a − t i × f b − S × f b dp_a-dp_b\leq t_i\times f_a+S\times f_a-t_i\times f_b-S\times f_b dpadpbti×fa+S×fati×fbS×fb
d p a − d p b ≤ ( t i + S ) × ( f a − f b ) dp_a-dp_b\leq (t_i+S)\times (f_a-f_b) dpadpb(ti+S)×(fafb)
d p a − d p b f a − f b ≤ t i + S \dfrac{dp_a-dp_b}{f_a-f_b}\leq t_i+S fafbdpadpbti+S

然后你会发现右边的是满足单调性的。
那我们就可以用单调队列来搞,来确定下凸图里面哪个最优。
(就把左边不满足的全踢出去,剩下的最左的那个就是最优的)

然后就搞斜率优化就好了。

代码

#include<cstdio>
#include<cstring>

using namespace std;

int n, s, t[5001], f[5001];
int st[5001], sf[5001], l, r;
int dp[5001], q[5001], x[5001], y[5001];

int main() {
	scanf("%d %d", &n, &s);
	for (int i = 1; i <= n; i++) {
		scanf("%d %d", &t[i], &f[i]);
		st[i] = st[i - 1] + t[i];//计算前缀和
		sf[i] = sf[i - 1] + f[i];
	}
	
	memset(dp, 0x7f, sizeof(dp));
	dp[0] = 0;
	l = r = 1;
	for (int i = 1; i <= n; i++) {
		while (l < r && y[q[l + 1]] - y[q[l]] <= (st[i] + s) * (x[q[l + 1]] - x[q[l]])) l++;//因为具有单调性,我们可以直接把前面不优的线段割掉
		dp[i] = dp[q[l]] + sf[i] * st[i] + s * sf[n] - st[i] * sf[q[l]] - s * sf[q[l]];//剩下的第一个就是最优的
		x[i] = sf[i];//记录这个点
		y[i] = dp[i];
		while (l < r && (y[i] - y[q[r]]) * (x[q[r]] - x[q[r - 1]]) <= (y[q[r]] - y[q[r - 1]]) * (x[i] - x[q[r]])) r--;//按斜率维护下凸图
		q[++r] = i;
	}
	
	printf("%d", dp[n]);
	
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值