B4156 [厦门小学生 C++ 2023] 太空旅行
本题是运筹学的经典模型——流水线调度作业模型的 Johnson 不等式的运用。其他的常见模型可参考 运营管理学习笔记-第十章。
Johnson 不等式是 S. M. Johnson 在 1954 年提出的用于解决两台机器流水车间调度问题以最小化最大完工时间的最优算法。它是一种贪心算法,步骤如下:
- 将所有
N 架飞船分成两个集合。集合S_1 包含所有满足U(i) \le V(i) 的飞船i 。集合S_2 包含所有满足U(i) > V(i) 的飞船i 。 - 对集合
S_1 中的飞船,按照它们的U(i) 值进行非递减 (升序) 排序。对集合S_2 中的飞船,按照它们的V(i) 值进行非递增 (降序) 排序。 - 最终的最优处理顺序是先按照排序后的顺序处理
S_1 中的所有飞船,然后紧接着按照排序后的顺序处理S_2 中的所有飞船。
这也就是其他题解中所写的:
这样做确实能完成本题。但是我不认为这个贪心是非常直观显然的。而这一个贪心的证明也并不简单,甚至可以说是十分繁琐。证明 Johnson 不等式可以使用邻项交换法完成。下面给出一个简要证明。
假设存在一个飞船处理序列
假设
对于飞船处理序列
代入上述数值可以得出:
对于飞船处理序列
代入上述数值可以得出:
下面我们证明:如果飞船
根据
-
C_{k+1, 2}(\pi) = \max(A+U(i)+U(j)+V(j), A+U(i)+V(i)+V(j), B+V(i)+V(j)) -
C_{k+1, 2}(\pi') = \max(A+U(j)+U(i)+V(i), A+U(j)+V(j)+V(i), B+V(j)+V(i))
由于
-
-
T_2-T_2'=[A+U(i)+V(i)+V(j)]-[A+U(j)+V(j)+V(i)]=U(i)-U(j)
由于
-
- 情况 1:$U(j)\leq V(i)$。此时 $U(i)>U(j)$ 成立,即 $T_2>T_2'$。而至于 $T_1-T_1'=V(j)-V(i)$,若 $V(j)\geq V(i)$ 则直接得证;若 $V(j)<V(i)$,我们希望此时的 $T_1'$ 不会成为交换后较大的候选值,因此应当比较 $T_2$ 和 $T_1'$。由于 $T_2-T_1'=[A+U(i)+V(i)+V(j)]-[A+U(j)+U(i)+V(i)]=V(j)-U(j)$,由于 $U(i)\leq V(j)$,$U(i)>U(j)$ 可以得出 $T_2-T_1'>0$,即 $T_2>T_1'$,且又有 $T_2>T_2'$,从而 $\max(T_1',T_2')\leq \max(T_1,T_2)$。 - 情况 2:$V(i)<U(j)$。此时 $U(i)>V(i)$ 成立。由于 $U(i)\leq V(j)$,因此有 $V(j)>V(i)$,可得 $T_1>T_1'$。至于 $T_2-T_2'=U(i)-U(j)$,若 $U(i)\geq U(j)$ 则直接得证;若 $U(i)<U(j)$ 则考虑 $T_1-T_2'=[A+U(i)+U(j)+V(j)]-[A+U(j)+V(j)+V(i)]=U(i)-V(i)$。此时因为 $U(i)>V(i)$,因此 $T_1>T_2'$。又有 $T_2>T_2'$,从而 $\max(T_1',T_2')\leq \max(T_1,T_2)$。 -
- 情况 1:$U(j)\leq V(i)$。证明与上方一致,从略。 - 情况 2:$V(i)<U(j)$。证明与上方一致,从略。
由于两个序列中第三个项完全相同,所以可得:
也即排序方式成立。因此,通过逐步交换所有违反 Johnson 不等式的相邻对
接下来从编写代码的角度出发,如果直接根据
- 将所有
N 架飞船分成两个集合。集合S_1 包含所有满足U(i) \le V(i) 的飞船i 。集合S_2 包含所有满足U(i) > V(i) 的飞船i 。 - 对集合
S_1 中的飞船,按照它们的U(i) 值进行非递减 (升序) 排序。对集合S_2 中的飞船,按照它们的V(i) 值进行非递增 (降序) 排序。
即可完成对飞船的排序,从而计算得出结论。从证明贪心方式到错误的排序,都体现出本题难度很高,并非部分用户所认为的只有黄题难度。
参考代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector <pair<int, int >> s(n);
for (int i = 0; i<n; i++)
cin >> s[i].first >> s[i].second;
vector <pair<int, int >> g1, g2;
for (int i = 0; i<n; i++) {
if (s[i].first <= s[i].second)
g1.push_back(s[i]);
else
g2.push_back(s[i]);
}
sort(g1.begin(), g1.end(), [](auto a, auto b) {
return a.first<b.first;
});
sort(g2.begin(), g2.end(), [](auto a, auto b) {
return a.second>b.second;
});
vector <pair<int, int >> ans;
for (auto s: g1)
ans.push_back(s);
for (auto s: g2)
ans.push_back(s);
long long t1 = 0, t2 = 0;
for (auto s: ans) {
t1 += s.first;
t2 = max(t1, t2) + s.second;
}
cout << t2 << endl;
return 0;
}