#P1078. 第2题-天文爱好者
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 1309
            Accepted: 367
            Difficulty: 4
            
          
          
          
                       所属公司 : 
                              美团
                                
            
                        
              时间 :2023年3月11日
                              
                      
          
 
- 
                        算法标签>扫描线算法          
 
第2题-天文爱好者
思路
将流星持续时间转为流星出现时间和消失时间,用同一个数组存下来并按照时间排序,之后依次遍历维护最大值和每个值的出现次数即可。最后输出最大值和最大值的出现次数。
需要注意的是,在处理同一时间的事件时应该先处理消失的再处理出现的,不然会让最大值错误。
代码
C++
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define f(i,l,r) for(int i=(l);i<=(r);i++)
#define pii pair<int,int>
#define fi first
#define se second
const int N =1e5+10;
int st[N], ed[N];
int n;
pii a[N];
int m;
int ans[N], mx, now;
int main()
{
	cin >> n;
	f(i, 1, n) cin >> st[i];
	f(i, 1, n) cin >> ed[i];
	f(i, 1 ,n) {
		a[m++] = {st[i], 1};
		a[m++] = {ed[i]+1, -1};
	}
	sort(a, a+m);
	f(i, 0, m-1) {
		if(a[i].se == -1) {
			mx = max(mx, now);
			if(i != 0) ans[now] += a[i].fi - a[i-1].fi;
			now--;
		} else {
			now++;
		}
	}
	cout << mx << ' ' << ans[mx] << endl;
}
Java
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] l = new int[n];
        int[] r = new int[n];
        for (int i = 0; i < n; i++) {
            l[i] = scanner.nextInt();
        }
        for (int i = 0; i < n; i++) {
            r[i] = scanner.nextInt();
        }
        TreeMap<Integer, Integer> mp = new TreeMap<>(); // 使用有序的哈希表进行离散化差分
        for (int i = 0; i < n; i++) {
            int a = l[i];
            int b = r[i];
            mp.put(a, mp.getOrDefault(a, 0) + 1);
            mp.put(b + 1, mp.getOrDefault(b + 1, 0) - 1);
        }
        TreeMap<Integer, Integer> cnts = new TreeMap<>(); // 统计观测到流星的频数
        int sum = 0;
        int pre = Arrays.stream(l).min().getAsInt();
        for (Map.Entry<Integer, Integer> entry : mp.entrySet()) {
            int u = entry.getKey();
            int v = entry.getValue();
            cnts.put(sum, cnts.getOrDefault(sum, 0) + (u - pre));
            sum += v;
            pre = u;
        }
        int maxCnt = 0;
        int maxNum = 0;
        for (Map.Entry<Integer, Integer> entry : cnts.entrySet()) {
            int u = entry.getKey();
            int v = entry.getValue();
            if (maxCnt < u) {
                maxCnt = u;
                maxNum = v;
            }
        }
        System.out.println(maxCnt + " " + maxNum);
    }
}
Python
n = int(input())
l = list(map(int, input().split()))
r = list(map(int, input().split()))
mp = {}  # 离散化差分
for i in range(n):
    a = l[i]
    b = r[i]
    mp[a] = mp.get(a, 0) + 1
    mp[b + 1] = mp.get(b + 1, 0) - 1
cnts = {}  # 统计观测到流星的频数
sum = 0
pre = min(l)
for u, v in sorted(mp.items()):
    cnts[sum] = cnts.get(sum, 0) + u - pre
    sum += v
    pre = u
maxCnt = 0
maxNum = 0
for u, v in cnts.items():
    if maxCnt < u:
        maxCnt = u
        maxNum = v
print(maxCnt, maxNum)
JS
let n = parseInt(readline())
let st = readline().split(' ').map(Number)
let ed = readline().split(' ').map(Number)
let a = [], m = 0
for(let i = 0 ; i < n ; i ++) {
    a[m++] = [st[i], 1]				//第一维a[i][0]存储时间,第二维a[i][1]存储出现或消失
    a[m++] = [ed[i]+1, -1]
}
a.sort((x, y) => (x[0] == y[0] ? x[1] - y[1] : x[0] - y[0]))		//先按照第一维排序,再按第二维排序
let ans = [], mx = 0, now = 0
for(let i = 0 ; i < m ; i ++) {
    if(a[i][1] == -1) {			//在每次流星即将消失的时候处理最大值,最大值时前一次一定是流星出现的事件,所以当前最大值的持续时间就是
        mx = Math.max(mx, now)																//a[i][0] - a[i-1][0]
        if(!ans[now]) {			//数组做字典用,如果字典中没有当前值,就初始化一下
            ans[now] = 0
        }
        ans[now] += a[i][0] - a[i-1][0]		//因为这里是消失事件,所以不可能是最早发生的,之前一定有一个出现事件比它早,所以a[i-1][0]不会越界
        now --
    } else {		//出现事件时不作处理,因为当前不可能是最大值
        now ++
    }
}
console.log(mx + " " + ans[mx])
        题目内容
小美一直对天文学充满热情,他的房子位于一个相对较为偏远的地方,没有大城市那样的光污染,因此他拥有非常好的观测条件。他每晚都会抬头仰望星空,寻找流星的痕迹,记录下每颗流星出现和消失的时刻。
现在他收集了 n 个流星在他所在观测地上空的出现时刻和消失时刻,对于一个流星,若其的出现时换为 s ,消失时刻为 t ,那么小美在时间段 [s,t] 都能够观测到它。对于一个时刻, 观测地上空出现的流星数量越多,则小美认为该时刻越好。
小美希望能够选择一个最佳的时刻进行观测和摄影,使他能观测到最多数量的流星,现在小美想知道,在这个最佳时刻,他最多能观测到多少个流星以及一共有多少个最佳时刻可供他选择。
输入描述
第一行是一个正整数 n ,表示流星的数量。
第二行是 n 个用空格隔开的正整数,第 i个数 si 表示第 i 个流星的出现时间。
第三行是 n 个用空格隔开的正整数,第 i 个数 ti 表示第 i 个流星的消失时间。
1≤n≤100000,1≤si≤ti≤105
原题面是1e9,思考如何做~
输出描述
输出一行用空格隔开的两个数 x 和 y ,其中 x 小美能观测到的最多流星数, y 表示可供他选择的最佳时刻数量。
样例
输入
3
2 1 5
6 3 7
输出
2 4
样例解释
在 7 个时刻中,时刻 2 、 2 、 5 、 6 可以观测到 2 个流星,其他时刻只能观测到 1 个流星,所以最多能观测到的流星数量为 2 ,最佳时刻的数是 4 。