题解 P2365 任务安排
题目描述
给定 \(n\) 个任务,每个任务有两个属性: \(t_i\) 和 \(c_i\) ,分别表示这个任务的时间和花费。
现在你需要把原本的任务分成若干批(不能改变原本的顺序),机器可以一次执行完这些任务,在执行每一批任务之前,需要 \(s\) 的准备时间,然后执行这一批任务的时间是所有任务的时间和。执行这一批任务的花费是结束时间 \(\times\) 所有任务的花费和。
请你求出最小的花费。
\(1 \leq n \leq 5000 ,1 \leq s \leq 50 ,1 \leq t_i ,c_i \leq 100\) 。
Solution
首先,我们设:
\[sc_i=\sum_{j=1}^i c_i \\
st_i=\sum_{j=1}^i t_i
\]
然后考虑动态规划:
设 \(f_i\) 表示执行完任务 \(1 \sim i\) 所需要的最小花费。
如果没有准备时间,那我们直接枚举 \(j = 1 \to i\) ,把 \([j,i]\) 分成一批任务,于是就有状态转移方程:
\[f_i = \min\{f_{j-1}+(sc_i-sc_{j-1})\times st_i \}
\]
但是有一个准备时间,我们可以转换一下:在第 \(j\) 个任务前要准备,等价于第 \(j\sim n\) 这些任务的持续时间都增长了 \(s\) ,那么花费就多了 \((st_n-st_{j-1}) \times s\) 。
于是我们可以修改状态转移方程为:
\[f_i = \min\{f_{j-1}+(sc_i-sc_{j-1})\times st_i +(st_n-st_{j-1}) \times s\}
\]
时间复杂度 \(\mathcal{O}(n^2)\) ,足够通过。
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
const int N = 5005;
inline int read() {
int num = 0 ,f = 1; char c = getchar();
while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
return num * f;
}
inline int min(int a ,int b) {return a < b ? a : b;}
int f[N] ,n ,s ,sc[N] ,st[N];
signed main() {
n = read(); s = read();
for (int i = 1; i <= n; i++) {
int t = read() ,c = read();
sc[i] = sc[i - 1] + c;
st[i] = st[i - 1] + t;
}
memset(f ,0x3f ,sizeof(f));
f[0] = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++)
f[i] = min(f[i] ,f[j - 1] + (sc[i] - sc[j - 1]) * st[i] + s * (sc[n] - sc[j - 1]));
printf("%d\n" ,f[n]);
return 0;
}