Wrapping Chocolate(multiset、贪心)
题意
给定\(n\)个巧克力,对于第\(i\)个巧克力,其宽是\(A_i\),长是\(B_i\)。
给定\(m\)个盒子,对于第\(i\)个盒子,其宽是\(C_i\),长是\(D_i\)。
若第\(i\)个巧克力能被第\(j\)个盒子装下,需要满足\(A_i \leq C_j\),并且\(B_i \leq D_j\)。
每个盒子最多只能装一个巧克力。
问所有巧克力是否都能被装下?
数据范围
\(1 \leq n \leq m \leq 2 \times 10^5\)
思路
首先这个题目一个初步的想法就是,巧克力和盒子都先按照宽从小到大排序。然后利用双指针,\(i\)表示当前扫描到的巧克力,\(j\)表示宽满足要求的第一个盒子(下标\(\geq j\)的盒子的宽也都是满足要求的)。
但是此时,下标从\(j\)到\(m\)的盒子的长是无序的,无法处理。那么,有没有什么方法可以自动排序呢,可以想到的是multiset。
这样的话,还是不好维护,因为随着\(j\)向后移动,multiset会不知道该弹出哪个元素。
那么,可以从大到小枚举,这样会将元素加入multiset中,而不是弹出。对于每个巧克力,找到multiset中满足要求的长中最小值,将其弹出即可。如果长的最大值也无法满足要求,那么当前的巧克力就不能被装下。
这里官方题解给出的方法大致相同,但是思路更加清晰。
将巧克力和盒子放在一起,按照宽从大到小排序,如果巧克力和盒子的宽相等,那么将盒子排在前面。
扫描这个序列,同时维护一个multiset。如果当前扫描到的是盒子,那么就将其长加入multiset中;如果是巧克力,找到满足要求的长的最小值,并弹出。如果找不到,则不能被装下。
注:multiset中维护的其实是宽满足要求的盒子的长。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
const int N = 400010;
int n, m;
struct Item
{
int w, l, type;
bool operator < (const Item &t) const
{
if(w == t.w) return type < t.type;
return w < t.w;
}
}a[N];
multiset<int> b;
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i].w);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i].l);
for(int i = 1; i <= n; i ++) a[i].type = 1;
for(int i = n + 1; i <= n + m; i ++) scanf("%d", &a[i].w);
for(int i = n + 1; i <= n + m; i ++) scanf("%d", &a[i].l);
for(int i = n + 1; i <= n + m; i ++) a[i].type = 2;
sort(a + 1, a + n + m + 1);
for(int i = n + m; i >= 1; i --) {
auto t = a[i];
if(t.type == 2) b.insert(t.l);
else {
int p = t.l;
auto loc = b.lower_bound(p);
if(loc == b.end()) {
printf("No\n");
return 0;
}
b.erase(loc);
}
}
printf("Yes\n");
return 0;
}