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;
}
posted @ 2022-03-27 21:34  pbc的成长之路  阅读(33)  评论(0编辑  收藏  举报