sweep line 扫描线模型

扫描线作用 & 算法步骤

扫描线可以讲区间分成不相交的区间,对于每个区间端点,可以利用+fee -fee操作(类似于差分、前缀和思想)来求解一些最优问题。
举栗子:一家餐厅一天会来很多客人,每个客人有一个arriveTime,leaveTime,我们想求出一天中,究竟同时又最多人的时候。

如上区间,可以看到最多人是时候是3个人,先按照arriveTime排个序,然后每次at时都要加一,每次lt时都要减一,最后一用一个变量求最值就行。

下面来一道题目:

题目链接:https://atcoder.jp/contests/abc188/tasks/abc188_d
思路:利用扫描线模型来考虑这个问题;首先不是有区间吗,譬如 1 ~ 2,是有两天的那么这个客人应该是第1天到,第3天离开,共两天(这里其实很难理解,有点类似物理,你12两天,不是有12 , 2~3 两个时间段吗?)。
那好了,区间已经定义好了,我们在L += c ,R -= c,就能够利用上面的模型了。
步骤如下:
1.先把所有区间的所有端点(左端点直接放,右端点+1再放),放到set中(去重)。然后每个端点都要对应的+c 或者-c操作(L 要+ ,R要减)。
2.为了做到上面的端点+-c,我们要做一个对应端点的映射,所以就要用到map做一个映射
3.把set中的端点拿出来,按时间线排序,然后对于每个端点用map找到+-c的操作,结果记录在acc中;用一个res += min(acc,C) * 区间长度(set,和排序其实是相当于够着了一个无重叠的区间,这个区间就要某一/几个服务的时间,acc记录了这个段总共要多少费用)。最后res就是结果

参考代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2 * 1e5 + 10;

set<int> s;
int a[N] ,b[N] , c[N];
map<int,int> ch;
int n,C;

signed main(){
	ios::sync_with_stdio(false);
	#ifndef  ONLINE_JUDGE
		freopen("tpl.txt","r",stdin);
	#endif
	cin >> n >> C;
	for(int i = 1; i <= n; ++i){
		cin >> a[i] >> b[i] >> c[i];
		s.insert(a[i]);
		s.insert(b[i] + 1);
		
		ch[a[i]] += c[i];
		ch[b[i] + 1] -= c[i];
	}
	
	
	vector<int> vc(s.begin(),s.end());
//	sort(vc.begin(),vc.end());
	int acc = 0;
	int res = 0;
	for(int i = 0; i < vc.size() - 1; i++){
		acc += ch[vc[i]];
		res += min(acc,C) * (vc[i+1] - vc[i]);
	}
	cout << res << endl;
	
	
	
}
posted @ 2021-01-16 12:04  cainiao11024  阅读(633)  评论(0编辑  收藏  举报