P5785 [SDOI2012]任务安排(斜率优化DP)

通过的第一道斜率优化

注:这并不是一篇教你具体怎样斜率优化的题解
因为我觉得太麻烦

题目传送门

题目描述

机器上有 n 个需要处理的任务,它们构成了一个序列。这些任务被标号为 1 到 n,因此序列的排列为 1,2,3⋯n。这 n 个任务被分成若干批,每批包含相邻的若干任务。从时刻 0 开始,这些任务被分批加工,第 i 个任务单独完成所需的时间是 Ti 。在每批任务开始前,机器需要启动时间 s,而完成这批任务所需的时间是各个任务需要时间的总和。
注意,同一批任务将在同一时刻完成。每个任务的费用是它的完成时刻乘以一个费用系数 Ci 。
请确定一个分组方案,使得总费用最小。

解题思路

首先可以列出一个很显然的DP式
F [ i ] [ j ] F[i][j] F[i][j]表示将前 i i i 个任务分成 j j j 批的最小花费
F [ i ] [ j ] = m i n ( F [ k ] [ ] j − 1 ] + ( S × j + s u m T [ i ] ) × ( s u m C [ i ] − s u m C [ k ] ) ) F[i][j]=min(F[k][]j-1]+(S\times j+sumT[i])\times (sumC[i]-sumC[k])) F[i][j]=min(F[k][]j1]+(S×j+sumT[i])×(sumC[i]sumC[k]))
其中sum分别为前缀和
但是这个式子很不优美,而且是二维的不好优化
所有我们把第二维删掉,但是这样就计算不了S了,但是我们可以变一下,改成S对后面任务的贡献
F [ i ] F[i] F[i]表示以第 i i i各任务为最后一个任务结尾的最小值

F [ i ] = m i n ( F [ j ] + s u m T [ i ] × ( s u m C [ i ] − s u m C [ j ] ) + S × ( s u m C [ N ] − s u m C [ j ] ) ) F[i]=min(F[j]+sumT[i]\times (sumC[i]-sumC[j])+S\times (sumC[N]-sumC[j])) F[i]=min(F[j]+sumT[i]×(sumC[i]sumC[j])+S×(sumC[N]sumC[j]))
这就是费用提前计算,也是常用的DP方式
建议认真理解之后再往后看
但是这还是 O ( n 2 ) O(n^2) O(n2)的,不行
所以就斜率优化就可以了
但是这个T可能不是单调递增的这就意味着我们不能用单调队列,而是要维护一个下凸壳,每次用二分查找转移点

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
const LL N = 4e5+7;
LL F[N],T[N],C[N],sumT[N],sumC[N],n;
LL X(LL p)
{
	return sumC[p];
}
LL Y(LL p)
{
	return F[p];
}
db slope(LL a,LL b)
{
	return 1.000*(Y(a)-Y(b))/(X(a)-X(b)); 
}
LL q[N];
LL S;
LL search(db k,LL L,LL R)
{
	if(L==R) return q[L];
	LL l=L,r=R;
	while(l<r)
	{
		LL mid=(l+r)>>1;
		if(slope(q[mid+1],q[mid])<=k) l=mid+1;
		else r=mid;
	}
	return q[l];
}
int main()
{
	scanf("%lld%lld",&n,&S);
	for(LL i=1;i<=n;i++)
	{
		scanf("%lld%lld",&T[i],&C[i]);
		sumT[i]=sumT[i-1]+T[i];
		sumC[i]=sumC[i-1]+C[i];
	}
	LL l=1,r=1;
	for(LL i=1;i<=n;i++)
	{
		LL j=search(S+sumT[i]*1.0000,l,r);
		F[i]=F[j]-(S+sumT[i])*sumC[j]+sumT[i]*sumC[i]+S*sumC[n];
		while(l<r&&slope(q[r],q[r-1])>=slope(i,q[r-1])) r--;
		q[++r]=i;
	}
	cout<<F[n];
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值