POJ 1180 任务安排2 斜率优化

题目描述:

在一台机器上有一N个任务要执行,顺序不得改变。任务划分为若干批>次,每批次由序列中的连续任务组成。处理从时间0开始,从第一批开始逐个处理。执行第i个任务所需的时间为Ti,执行每批任务前机器需要S的时间启动,完成每一批任务所需的时间为S+这一批任务的所需时间和。且这一批次中所有任务的完成时间都相同,为这一批任务的完成时间。
完成每个任务的费用为 完成时刻*费用系数Ci。请为这些任务分组,使总费用最小。

输入:

5
1
1 3
3 2
4 3
2 3
1 4

输出:

153

数据范围:

1<=N<=10000,1<=S<=50,1<=Ti,Ci<=100

题解部分:

O(N2)算法(部分分):

运用“费用提前计算”的思想

设:
F [ i ] F[i] F[i]为 把前i个任务分成若干批次的最小费用
s u m C [ i ] sumC[i] sumC[i] s u m T [ i ] sumT[i] sumT[i] T [ i ] T[i] T[i] C [ i ] C[i] C[i]的前缀和

得到F[i]的动态转移方程:
F [ i ] = F[i]= F[i]=min0<=j<=i{ 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[j]+sumT[i]*(sumC[i]-sumC[j])+S*(sumC[n]-sumC[j]) F[j]+sumT[i](sumC[i]sumC[j])+S(sumC[n]sumC[j])}

最终 F [ n ] F[n] F[n]为所得结果

O(N)算法(满分):

讲O(N2)算法的动态转移方程进行移项,并且将常数,只与 i i i有关,只与 j j j有关 和 与 i i i j j j都有关的项分开得:

F [ i ] = F[i]= F[i]=min0<=j<=i{ F [ j ] − ( s u m T [ i ] + S ) ∗ s u m C [ j ] F[j]-(sumT[i]+S)*sumC[j] F[j](sumT[i]+S)sumC[j]} + s u m T [ i ] ∗ s u m C [ i ] + S ∗ s u m C [ n ] +sumT[i]*sumC[i]+S*sumC[n] +sumT[i]sumC[i]+SsumC[n]

若将min函数去掉,将有关于j的值 F [ j ] F[j] F[j] s u m C [ j ] sumC[j] sumC[j]看做纵坐标和横坐标可得:

F [ j ] = ( S + s u m T [ i ] ) ∗ s u m C [ j ] + F [ i ] − s u m T [ i ] ∗ s u m C [ i ] − S ∗ s u m C [ n ] F[j]=(S+sumT[i])*sumC[j]+F[i]-sumT[i]*sumC[i]-S*sumC[n] F[j]=(S+sumT[i])sumC[j]+F[i]sumT[i]sumC[i]SsumC[n]

可将该方程看作一次函数,斜率为 ( S + s u m T [ i ] ) (S+sumT[i]) (S+sumT[i]),截距为 F [ i ] − s u m T [ i ] ∗ s u m C [ i ] − S ∗ s u m C [ n ] F[i]-sumT[i]*sumC[i]-S*sumC[n] F[i]sumT[i]sumC[i]SsumC[n]

对于特定的 i i i所得到的的一次函数,斜率不变,当截距最小时得到最优解。

而对于坐标轴上的任意三个点有两种情况:
上凸下凸
上凸 下凸
当出现上凸时, j j j2必然不能成为最优解。
即保证 j j j2与其相邻的两个点的斜率递增时, j j j2有可能成为最优解。

于是可以借助 单调队列优化DP 的思想,维护一个 相邻两点的斜率 单调递增的下凸壳。

对于某一个 i i i,是 F [ i ] F[i] F[i]取到最小值的点,满足与上一个点的斜率小于 S + s u m T [ i ] S+sumT[i] S+sumT[i],与下一个点的斜率大于 S + s u m T [ i ] S+sumT[i] S+sumT[i]


然后,对于每一个 i i i,因为 S + s u m T [ i ] S+sumT[i] S+sumT[i]是单调递增的,所以对于最优解之前的所有选项都可以直接排除,即删除队首元素,直到碰到正解(斜率大于 S + s u m T [ i ] S+sumT[i] S+sumT[i]的点)

预处理后,对于每个i都可以在 O ( 1 ) O(1) O(1)的时间内获取答案

正解代码:

#include <bits/stdc++.h>
using namespace std;
const int N=300005;
int n,s,l,r,f[N],q[N],st[N],sc[N];
int main(){
//	freopen("1.in","r",stdin);
	scanf ("%d%d",&n,&s);
	for (int i=1;i<=n;++i){
		int t,c;
		scanf ("%d%d",&t,&c);
		st[i]=st[i-1]+t;
		sc[i]=sc[i-1]+c;
	}
	l=1;
	r=1;
	f[0]=0;
	q[1]=0;
	for (int i=1;i<=n;++i){
		while (l<r && (f[q[l+1]]-f[q[l]])<=(st[i]+s)*(sc[q[l+1]]-sc[q[l]]))l++;
		f[i]=f[q[l]]-(s+st[i])*sc[q[l]]+st[i]*sc[i]+s*sc[n];
		while (l<r && (f[q[r]]-f[q[r-1]])*(sc[i]-sc[q[r]])>=(f[i]-f[q[r]])*(sc[q[r]]-sc[q[r-1]]))r--;
		q[++r]=i;
	}
	printf ("%d\n",f[n]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值