CCF CSP第一次认证1.5 任务调度 题解 动态规划

题目描述

有若干个任务需要在一台机器上运行。它们之间没有依赖关系,因此,可以被按照任意顺序执行。

该机器有两个 CPU 和一个 GPU。对于每个任务,你可以为它分配不同的硬件资源:

  1. 在单个 CPU 上运行。
  2. 在两个 CPU 上同时运行。
  3. 在单个 CPU 和 GPU 上同时运行。
  4. 在两个 CPU 和 GPU 上同时运行。

一个任务开始执行以后,将会独占它所用到的所有硬件资源,不得中断,直到执行结束为止。第 \(i\) 个任务用单个 CPU,两个 CPU,单个 CPU 加 GPU,两个 CPU 加 GPU 运行所消耗的时间分别为 \(a_i\)\(b_i\)\(c_i\)\(d_i\)

现在需要你计算出至少需要花多少时间可以把所有给定的任务完成。

输入格式

输入的第一行只有一个正整数 \(n(1 \le n \le 1000)\), 是总共需要执行的任务个数。

接下来的 \(n\) 行每行有四个正整数 \(a_i\), \(b_i\), \(c_i\), \(d_i\)\((1 \le a_i, b_i, c_i, d_i \le 10)\), 以空格隔开。

输出格式

输出只有一个整数,即完成给定的所有任务所需的最少时间。

样例输入

3
4 4 2 2
7 4 7 4
3 3 3 3

样例输出

7

样例解释

有很多种调度方案可以在 \(7\) 个时间单位里完成给定的三个任务,以下是其中的一种方案:

同时运行第一个任务(单 CPU 加上 GPU)和第三个任务(单 CPU), 它们分别在时刻 \(2\) 和时刻 \(3\) 完成。在时刻 \(3\) 开始双 CPU 运行任务 \(2\),在 时刻 \(7\) 完成。

题目分析

本题的解决实际上可以分为两个步骤:

  1. 为每个任务在 \(4\) 种硬件资源中选择适当的一种;
  2. 安排所有任务的执行顺序。

先对步骤1进行分析,不难发现,硬件资源的分配模式中存在某些微妙的关系,如下图所示:

执行方案 不占用GPU 占用GPU
不占用CPU 不可行 不可行
占用一个CPU \(a_i\) \(c_i\)
占用两个CPU \(b_i\) \(d_i\)

可以得出以下规律和性质:

  • 程序运行至少会占用一个CPU。
  • 程序可能张勇更多资源且花费更多时间,这种情况不是优化的,需要排除。

由于一共只有两个CPU,每个程序又至少会占用一个,所以最多有两个程序同时运行。而且,如果有两个程序同时执行,则这两个程序分配到的最远必须均为一个CPU。为了方便表述,将占用 \(x\) 个CPU和 \(y\) 个GPU的方案记为 \(xCyG\),至此又会得到以下结论:

  • \(2C0G\)\(2C1G\) 都意味着不可能与其它程序并行,所以可以合并,时间取较小值。
  • \(1C0G\) 可以和 \(1C1G\) 并行,两个 \(1C0G\) 也可以并行,但是两个 \(1C1G\) 不能并行。

因此,可以将两个CPU分梨,认为其中一个CPU附带GPU,这并不会对答案造成影响,即模型被修改为如下形式:

  • 有一个 CPU+GPU(将这二者视为整体)和一个 CPU。
  • 每个任务可以选择占用 CPU+GPU、CPU 或者 两个都占用,这 \(3\) 种占用方式占用的时间不同。

接下来考虑任务顺序。可以发现,对于占用全部资源的任务,可以调度其在最开始就运行。这样,在开始的一段时间,资源是满载的,不需要等待资源释放就可以运行。而对于其它任务,由于任务之间没有依赖关系,而又分别只占用一个 CPU+GPU 或 一个CPU,令其依次执行即可。

至此,可以设计一个动态规划来完成这一过程。令 \(f_{i,j}\) 表示为了完成前 \(i\) 个任务占用 CPU+GPU 时间为 \(j\) 时不附带 GPU 的 CPU 的最小时间。这样,初始状态显然为 \(f_{0,0} = 0\)。对于在此后的每个状态,状态转移方程为:

\[f_{i,j} = \min \begin{cases} f_{i-1,j} + a_i \\ f_{i-1,j-c_i} & (j \ge c_i) \\ f_{i-1, j - \min\{b_i,d_i\}} + \min\{ b_i,d_i \} & (j \ge \min\{ b_i,d_i \}) \end{cases}\]

这样,答案等于 \(\min\limits{j} \max\{ f_{n,j},j \}\)

此解法的时间复杂度为 \(O(n^2 \times \max\limits_{i=1,2,\ldots,n} \{ a_i, b_i, c_i, d_i \})\)

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int n, a, b, c, d, f[maxn][maxn*10], ans = 0x3f3f3f3f;

int main() {
    cin >> n;
    memset(f, 0x3f, sizeof(f));
    f[0][0] = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a >> b >> c >> d;
        c = min(c, a);
        b = min(min(a, b), min(c, d));
        for (int j = 0; j <= i*10; j++) {
            f[i][j] = f[i-1][j] + a;
            if (j >= c) f[i][j] = min(f[i][j], f[i-1][j-c]);
            if (j >= b) f[i][j] = min(f[i][j], f[i-1][j-b] + b);
        }
    }
    for (int i = 0; i <= n*10; i++)
        ans = min(ans, max(i, f[n][i]));
    cout << ans << endl;
    return 0;
}
posted @ 2022-07-31 00:11  quanjun  阅读(326)  评论(0编辑  收藏  举报