#贪心
## 题目大意
有 $N$ 个主菜,第 $i$ 个主菜需要 $a_i$ 元;$M$ 个配菜,第 $i$ 个配菜 $b_i$ 元。但是有 $L$ 对主菜和配菜是不能配在一起的。一对菜的价值定义为主菜加上配菜价钱的和。问你最贵的一对菜需要多少元。
## 思路
我们先考虑一个暴力算法:首先枚举 $N$ 个主菜,然后枚举 $M$ 个配菜,最后遍历 $L$ 个配对,判断一下是否可以购买,然后记录最大值。这种算法的时间复杂度是 $\mathcal O(NML)$ 的,而 $1\le N,M,L\le 10^5$,使用这种算法会直接吃席。
我们可以来尝试优化一下,既然要求的是最大值,那么一个主菜必须配对可以买的的最大价值的配菜。那么我们可以直接对 $b$ 数组排序,然后从后往前遍历一遍,在来判断是否可以购买,如果可以就记录最大值,然后 break。这种做法虽然是三重循环,但是时间复杂度是 $\mathcal O(L^2)$ 的,因为第一二层循环只会枚举 $L$ 次,枚举完就必定可以找到可以配对的。
现在我们要加快判断是否能够购买的速度。可以使用一个集合,然后第三重循环就可以直接省掉——变成一个判断是否在集合内出现过的问题。这种算法的时间复杂度是 $\mathcal O(L\log_2\min(M,L))$ 的,因为一个集合内的元素最多有 $L$ 个,但是其实同样不能超过 $M$ 个,因此这题就过掉了。
## 代码
```cpp
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
int n, m, l, ans;
int main() {
cin >> n >> m >> l;
vector<int> a(n + 1); // 主菜
vector<pair<int, int>> b(m + 1); // 配菜
vector<set<int>> s(n + 1, set<int>()); // 不能配对的菜
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
cin >> b[i].first, b[i].second = i; // 输入并打上编号
}
sort(b.begin(), b.end(), [](const auto &a, const auto &b) { // 排序
return a.first < b.first; // 按价格排序
});
for (int i = 1, x, y; i <= l; i++) {
cin >> x >> y;
s[x].insert(y); // 买了主菜 x 不能买 y
}
for (int i = 1; i <= n; i++) { // 枚举主菜
for (int j = m; j >= 1; j--) { // 枚举配菜
if (!s[i].count(b[j].second)) { // 如果可以购买
ans = max(ans, a[i] + b[j].first); // 记录最大值
break; // 灵魂剪枝
}
}
}
cout << ans << '\n'; // 输出最优答案
return 0;
}
```