1. 程式人生 > 其它 >Wrapping Chocolate(multiset、貪心)

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;
}