10184. 「一本通 5.6 例 1」任务安排 1 & 10185. 「一本通 5.6 例 2」任务安排 2 & 10186. 「一本通 5.6 例 3」任务安排 3


由于这三道题都是同一道题,只是数据范围不同,故拉成同一篇博客

题意

有$n$个任务,机器人($jxc$)会把这$n$个任务分成若干批,从时刻$0$开始,任务将被分批加工,执行第$i$个任务需要花费时间为$T_i$。另外,在每一批任务开始前,机器需要$S$的启动时间。

同一批任务将在同一时刻完成。每个任务的费用$=$它完成的时间$\times$一个费用的系数$C_i$。

思路

对于$n \leq 5000$的数据

时间复杂度$O(N^2)$

设$f[i]​$表示前$i​$个任务所花费最小价值。

初始化:

$$f[i]=inf|1<=i<=n,f[0]=0​$$

转移方程:

$$f[i]=min \left{f[j-1]+S(c[n]-c[j-1])+t[i](c[i]-c[j-1])|1<=j<=i \right}​$$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<bits/stdc++.h>
using namespace std;
int n,S,f[5010],t[5010],c[5010];
int main(){
cin>>n>>S;
for(int i=1;i<=n;i++){
int x,y;
cin>>x>>y;
t[i]=t[i-1]+x;
c[i]=c[i-1]+y;
}
memset(f,63,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
f[i]=min(f[j-1]+S*(c[n]-c[j-1])+t[i]*(c[i]-c[j-1]),f[i]);
}
}
cout<<f[n]<<endl;
return 0;
}

对于$1\leq N \leq 10^4$的数据

时间复杂度为$O(N)$

转移方程同上,
$$f[i]=min \left{ f[j-1]+S(c[n]-c[j-1])+t[i](c[i]-c[j-1])|1<=j<=i \right }​$$

将只含有$i​$的项移出,得:

$$f[i]=min \left { f[j-1]-Sc[j-1]-t[i]c[j-1]|1<=j<=i \right }+Sc[n]+t[i]c[i]​$$

合并同类项,得:

$$f[i]=min \left { f[j]-(S+t[i])c[j]|0<=j<=i-1 \right }+Sc[n]+t[i]*c[i]$$

设$y=f[j]$,$k=S+t[i]$,$x=c[j]$,$b=Sc[n]+t[i]c[i]$

那么:

$$f[i]=y-kx+b$$

即:
$$y=kx+(f[i]-b)$$

  • 斜率优化一:
    因为$t[i]$在递增,斜率$k$在变大,直线越来越陡,所以队首的两个点之间斜率至少要大于$(S+t[i])$。
    即:$(f[q[l+1]]-f[q[l]])/(c[q[l+1]]-c[q[l]])>(S+t[i])$
    即:若$(f[q[l+1]]-f[q[l]])/(c[q[l+1]]-c[q[l]])<=(S+t[i])$,队首弹出
    化简,得:若$(f[q[l+1]]-f[q[l]])<=(S+t[i])*(c[q[l+1]]-c[q[l]])$,队首弹出

  • 斜率优化二:
    确保右侧的斜率大于左侧的斜率,即:$(f[q[r]]-f[q[r-1]])/(c[q[r]]-c[q[r-1]])<(f[i]-f[q[r]])/(c[i]-c[q[r]])​$。
    若$(f[q[r]]-f[q[r-1]])/(c[q[r]]-c[q[r-1]])>=(f[i]-f[q[r]])/(c[i]-c[q[r]])​$,队尾弹出。
    化简,得:若$(f[i]-f[q[r]])(c[q[r]]-c[q[r-1]])<=(f[q[r]]-f[q[r-1]])(c[i]-c[q[r]])​$,队尾弹出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<bits/stdc++.h>
using namespace std;
int n,S,f[300010],t[300010],c[300010],q[300010],l,r;
int main(){
cin>>n>>S;
for(int i=1;i<=n;i++){
int x,y;
cin>>x>>y;
t[i]=t[i-1]+x;
c[i]=c[i-1]+y;
}
memset(f,63,sizeof(f));
f[0]=0;
l=1,r=1;
for(int i=1;i<=n;i++){
int k=S+t[i];
while(l<r&&f[q[l+1]]-f[q[l]]<=k*(c[q[l+1]]-c[q[l]])) l++;
f[i]=f[q[l]]-k*c[q[l]]+t[i]*c[i]+S*c[n];
while(l<r&&(f[i]-f[q[r]])*(c[q[r]]-c[q[r-1]])<=(f[q[r]]-f[q[r-1]])*(c[i]-c[q[r]])) r--;
q[++r]=i;
}
cout<<f[n]<<endl;
return 0;
}

对于$1 \leq N \leq 3 \times 10^5 , \left| T_i \right|<2^8$的数据

虽然添加了单调队列的优化,但是时间可以为负数,所以之前维护的单调性就不存在了,所以我们需要使用二分+单调队列来维护斜率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<bits/stdc++.h>
using namespace std;
long long n,S,f[300010],t[300010],c[300010],q[300010],l,r,L,R;
long long Cut(long long j){return f[j]-c[n]*t[j]+c[j]*t[j]-c[j]*S;}
int main(){
cin>>n>>S;
for(long long i=1;i<=n;i++){
long long x,y;
cin>>x>>y;
t[i]=t[i-1]+x;
c[i]=c[i-1]+y;
}
memset(f,127,sizeof(f));
f[0]=0;
L=0,R=0;
for(long long i=1;i<=n;i++){
l=0,r=R-1;long long ans=R;
while(l<=r){
long long mid=(l+r)>>1;
if((c[q[mid+1]]-c[q[mid]])*t[i]<=Cut(q[mid+1])-Cut(q[mid])) ans=mid,r=mid-1;
else l=mid+1;
}
long long j=q[ans];
f[i]=f[j]+(c[n]-c[j])*(t[i]-t[j]+S);
while(L<R&&(Cut(q[R])-Cut(q[R-1]))*(c[i]-c[q[R]])>=(Cut(i)-Cut(q[R]))*(c[q[R]]-c[q[R-1]])) R--;
q[++R]=i;
}
cout<<f[n]<<endl;
return 0;
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×