题目描述:
在一台机器上有一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]+S∗sumC[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]−S∗sumC[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]−S∗sumC[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;
}