两类简单题目引入扫描线思想
题目一
1851. 包含每个查询的最小区间 - 力扣(LeetCode)
我们将询问数组按从小到大排序,每个询问点视为扫描线。
将区间排序,每次来到一个询问点 x x x 时,将所有 l ≤ x l \leq x l≤x 的区间加入小根堆。
加入之后,将所有 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;
}
题目三
题目三与题目二的区别在于输出方式的区别以及建筑的范围不同。
本题就不能一个点一个点扫描。
不妨考虑扫描哪些 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;
}
};