两类简单题目引入扫描线思想

两类简单题目引入扫描线思想

题目一

1851. 包含每个查询的最小区间 - 力扣(LeetCode)

我们将询问数组按从小到大排序,每个询问点视为扫描线。

将区间排序,每次来到一个询问点 x x x 时,将所有 l ≤ x l \leq x lx 的区间加入小根堆。

加入之后,将所有 r > x r > x r>x 的区间移出小根堆。

堆顶即是答案。

小根堆维护 : 区间长度,区间右端点

class Solution {
public:
    vector<int> minInterval(vector<vector<int>>& intervals, vector<int>& queries) {
        sort(intervals.begin(), intervals.end(), [&] (auto &a, auto &b){
            return a[0] < b[0];
        });
        vector<pair<int, int> > nq;
        vector<int> ans(queries.size());
        for(int i = 0; i < queries.size(); i ++){
            nq.push_back({queries[i], i});
        }
        sort(nq.begin(), nq.end(), [&](auto &a, auto &b){
            return a.first < b.first;
        });
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>> > heap; // 长度, 右端点
        int now = 0;
        for(int i = 0; i < nq.size(); i ++){
            auto &[p, id] = nq[i];
            while(now < intervals.size() && p >= intervals[now][0]){
                heap.push({intervals[now][1] - intervals[now][0] + 1, intervals[now][1]});
                now ++;
            }
            while(heap.size() && p > heap.top().second) heap.pop();
            if(heap.size()) ans[id] = heap.top().first;
            else ans[id] = -1;
        }
        return ans;
    }
};

题目二

P1904 天际线 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

不难发现,所有折点出现在高度发生变化的点。

每次高度变化的那条竖线,就对应着两个折点。

因为建筑的 [l, r] 区间范围比较小,我们直接扫描每个 x = i x=i x=i 的竖线,当检测到高度发生变化时,就会产生两个折点。

#include<bits/stdc++.h>
using namespace std;
#define int long long

class Solution {
public:
    vector<int> getSkyline(vector<vector<int>>& buildings) {
        vector<int> ans;
        for(auto &vc : buildings) vc[1] --;
        sort(buildings.begin(), buildings.end(), [&] (auto &a, auto &b){
            return a[0] < b[0];
        });
        int l = buildings[0][0], r = -1;
        for(auto &vc : buildings) r = max(r, vc[1]); 
        priority_queue<pair<int, int> > heap;
        int s = 1;
        int now = 0, las = 0;
        for(int i = l; i <= r + 1; i ++){
            while(now < buildings.size() && i >= buildings[now][0]){
                heap.push({buildings[now][2], buildings[now][1]});
                now ++;
            }
            while(heap.size() && i > heap.top().second) heap.pop();
            int tmp = heap.size() ? heap.top().first : 0;
            if(tmp != las) ans.push_back(i), ans.push_back(tmp), las = tmp;
        } 
        return ans;
    }
};

void solve(){
    vector<vector<int> > vc;
    int a, b, c;
    while(cin >> a >> b >> c){
        vc.push_back({a, c, b});
    }
    vector<int> ans;
    Solution s;
    ans = s.getSkyline(vc);
    for(auto &nx : ans) cout << nx << ' ';
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0); 
    solve();
    return 0;
}

题目三

218. 天际线问题 - 力扣(LeetCode)

题目三与题目二的区别在于输出方式的区别以及建筑的范围不同。

本题就不能一个点一个点扫描。

不妨考虑扫描哪些 x = i x=i x=i ,其实扫描每个建筑的左右端点再加上两个建筑中间的空白点就可以了,至于怎么添加这些空白点,见代码。

题目让我们输出的就是高度发生变化的那些点。


class Solution {
public:
    vector<vector<int>> getSkyline(vector<vector<int>>& buildings) {
        vector<vector<int> > ans;
        for(auto &vc : buildings) vc[1] --;
        sort(buildings.begin(), buildings.end(), [&] (auto &a, auto &b){
            return a[0] < b[0];
        });
        
        // nx : 所有要扫描的点
        vector<int> nx, v;
        for(auto &vc : buildings) nx.push_back(vc[0]), nx.push_back(vc[1]);
        sort(nx.begin(), nx.end()); 
        for(int i = 0; i + 1 < nx.size(); i ++){
            if(nx[i + 1] - nx[i] > 1) v.push_back(nx[i] + 1);
        }
        for(auto &e : v) nx.push_back(e); 
        nx.erase(unique(nx.begin(), nx.end()), nx.end()); 
        sort(nx.begin(), nx.end()); 
        nx.push_back(nx.back() + 1);

        priority_queue<pair<int, int> > heap;
        int now = 0, las = 0;
        for(int I = 0; I < nx.size(); I ++){
            int i = nx[I]; 
            while(now < buildings.size() && i >= buildings[now][0]){
                heap.push({buildings[now][2], buildings[now][1]});
                now ++;
            }
            while(heap.size() && i > heap.top().second) heap.pop();
            int tmp = heap.size() ? heap.top().first : 0;  
            if(tmp != las) ans.push_back({i, tmp}), las = tmp;
        }
        
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值