2 条题解
-
0
题解
思路分析
这是一道 二分答案 + DP + 贪心 的任务调度优化问题。
问题建模
- 个计算节点处理 个 A 类任务和 个 B 类任务
- 每个节点有启动时间和计算时间(随任务数平方增长)
- 目标:最小化最后完成的节点的完成时间
核心思路
1. 二分答案
二分最大完成时间 ,判断能否在时间 内完成所有任务。
2. DP 判断可行性
对于固定的 ,判断每个节点能处理的任务组合。
定义 表示节点 处理 个 A 类任务、 个 B 类任务,上一个任务类型为 (0=A, 1=B),所需的最小时间。
状态转移:
- 从 转移:先做 B 类,再做 个 A 类$$f[i][a][b][0] = f[i][a-x][b][1] + t_i^A + k_i^A \cdot x^2 $$
- 从 转移:先做 A 类,再做 个 B 类$$f[i][a][b][1] = f[i][a][b-y][0] + t_i^B + k_i^B \cdot y^2 $$
3. 背包DP分配任务
定义 表示前 个节点处理了 个 A 类任务后,最多能处理多少 B 类任务。
对于每个节点 ,枚举它能处理的 组合(满足 ):
4. 判断可行性
如果 ,说明在时间 内能完成所有任务。
算法步骤
-
预处理每个节点的时间函数:
- 对每个节点 ,预处理
-
二分答案:
- 二分时间
- 使用 DP 判断可行性
-
背包DP验证:
- 枚举节点和任务分配
- 判断是否满足
-
输出答案
复杂度分析
- 预处理:
- 二分:
- DP验证:
- 总时间复杂度:
- 空间复杂度:
#include <bits/stdc++.h> using namespace std; const int MAX_P = 20 + 5; const int MAX_A = 60 + 5; const int INF32 = 1e9; int A, B, P; int f[MAX_P][MAX_A][MAX_A][2]; int g[MAX_P][MAX_A]; int main() { scanf("%d%d%d", &A, &B, &P); for (int i = 1; i <= P; i ++) { int ta, tb, ka, kb; scanf("%d%d%d%d", &ta, &tb, &ka, &kb); for (int a = 0; a <= A; a ++) { for (int b = 0; b <= B; b ++) { if (a + b == 0) continue; f[i][a][b][0] = INF32; f[i][a][b][1] = INF32; for (int x = 1; x <= a; x ++) { f[i][a][b][0] = min(f[i][a][b][0], f[i][a - x][b][1] + ta + ka * x * x); } for (int y = 1; y <= b; y ++) { f[i][a][b][1] = min(f[i][a][b][1], f[i][a][b - y][0] + tb + kb * y * y); } // printf("f[%d][%d][%d]=%d,%d\n", i, a, b, f[i][a][b][0], f[i][a][b][1]); } } // printf("\n"); } int l = 0, r = INF32; while (l < r) { int mid = l + r >> 1; for (int a = 0; a <= A; a ++) { g[0][a] = -INF32; } g[0][0] = 0; for (int i = 1; i <= P; i ++) { for (int a = 0, b; a <= A; a ++) { g[i][a] = g[i - 1][a]; } for (int x = 0; x <= A; x ++) { int y = B; for (; y >= 0 && f[i][x][y][0] > mid && f[i][x][y][1] > mid; y --); // printf("i=%d,x=%d,y=%d\n", i, x, y); if (y >= 0) { for (int a = 0; a <= A; a ++) { g[i][a] = max(g[i][a], g[i - 1][a - x] + y); } } } // for (int a = 0; a <= A; a ++) { // printf("g[%d][%d]=%d\n", i, a, g[i][a]); // } } // printf("mid=%d g[P][A]=%d\n", mid, g[P][A]); if (g[P][A] >= B) { r = mid; } else { l = mid + 1; } } printf("%d\n", l); return 0; } -
0
题解
把问题理解成“给定一个截止时间 ,能否在不超过 的时间内完成全部 类、 类子任务”。如果我们可以判定这一命题是否成立,再对 二分即可。
对每个计算节点单独做 DP,预先算出
f[i][a][b][last]:表示第i个节点连续执行 个 子任务和 个 子任务,并以last指示的类型结束时所需的最短时间。状态转移十分直接:加一个同类型任务就是在当前时间基础上累加 这样的公式,加一个不同类型任务需要强制切换工作模式(用另一个last)。由于 ,这个 DP 的规模可以接受。随后对答案进行二分。给定猜测的时间上限
mid,我们再做一次类似背包的 DP:g[i][a]表示使用前 个节点,最多可以完成多少个 任务,同时恰好完成 个 任务。枚举当前节点分配到的 个 任务,找到它在时间不超过mid的情况下最多还能完成多少个 任务(即在预处理的f[i][x][y][*]中寻找任一不超过mid的 ),于是我们就能更新g。如果最终 ,说明给出的时间足够,否则就需要增大二分的上界。这样就能在 的 DP 预处理和 的判定内求出最小完成时间。
#include <bits/stdc++.h> using namespace std; const int MAX_P = 20 + 5; const int MAX_A = 60 + 5; const int INF32 = 1e9; int A, B, P; int f[MAX_P][MAX_A][MAX_A][2]; int g[MAX_P][MAX_A]; int main() { scanf("%d%d%d", &A, &B, &P); for (int i = 1; i <= P; i ++) { int ta, tb, ka, kb; scanf("%d%d%d%d", &ta, &tb, &ka, &kb); for (int a = 0; a <= A; a ++) { for (int b = 0; b <= B; b ++) { if (a + b == 0) continue; f[i][a][b][0] = INF32; f[i][a][b][1] = INF32; for (int x = 1; x <= a; x ++) { f[i][a][b][0] = min(f[i][a][b][0], f[i][a - x][b][1] + ta + ka * x * x); } for (int y = 1; y <= b; y ++) { f[i][a][b][1] = min(f[i][a][b][1], f[i][a][b - y][0] + tb + kb * y * y); } // printf("f[%d][%d][%d]=%d,%d\n", i, a, b, f[i][a][b][0], f[i][a][b][1]); } } // printf("\n"); } int l = 0, r = INF32; while (l < r) { int mid = l + r >> 1; for (int a = 0; a <= A; a ++) { g[0][a] = -INF32; } g[0][0] = 0; for (int i = 1; i <= P; i ++) { for (int a = 0, b; a <= A; a ++) { g[i][a] = g[i - 1][a]; } for (int x = 0; x <= A; x ++) { int y = B; for (; y >= 0 && f[i][x][y][0] > mid && f[i][x][y][1] > mid; y --); // printf("i=%d,x=%d,y=%d\n", i, x, y); if (y >= 0) { for (int a = 0; a <= A; a ++) { g[i][a] = max(g[i][a], g[i - 1][a - x] + y); } } } // for (int a = 0; a <= A; a ++) { // printf("g[%d][%d]=%d\n", i, a, g[i][a]); // } } // printf("mid=%d g[P][A]=%d\n", mid, g[P][A]); if (g[P][A] >= B) { r = mid; } else { l = mid + 1; } } printf("%d\n", l); return 0; }
- 1
信息
- ID
- 3253
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者