题解 P2365 任务安排

题目描述

Link

给定 \(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;
}
posted @ 2021-02-23 16:45  recollector  阅读(76)  评论(0编辑  收藏  举报