【贪心】选择尽量多的不相交区间

参考链接

问题:跟定n个开区间(a,b)或闭区间[a,b],选择尽量多个区间,且这些区间互不相交。

贪心策略:将每个区间按右端点从小到大排序。遍历所有的区间,如果当前区间的左端点和最长区间的右端点相冲突,直接放弃,否则选取。
模版题

AC代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
struct qj{
	int s,f;
	friend bool operator<(qj x,qj y){
		if(x.f==y.f) return x.s<y.s;
		return x.f<y.f;
	}
}a[maxn]; 
int n,ans,last;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i].s>>a[i].f;
	sort(a+1,a+n+1);
	last=1,ans=1;
	for(int i=2;i<=n;i++){
		if(a[i].s>=a[last].f){
			++ans;
			last=i;
		}
	}
	cout<<ans;
    return 0;
}

进阶题1

这题我们根据小岛和半径确定区间的左端点和右端点。然后套模版选择区间。但这道题有一个小坑是如果半径d<y,是覆盖不到的,输出-1。

AC代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int n,d,c;
struct node{
	double s,f;
	friend bool operator<(node a,node b){
		return a.f<b.f;
	}
}pos[1005];
int main(){
	cin>>n>>d;
	while(n&&d){
		++c;
		bool flag=0;
		for(int i=1;i<=n;i++){
			int x,y;
			cin>>x>>y;
			if(y>d) flag=1;
			pos[i].s=x*1.0-sqrt(d*d-y*y);
			pos[i].f=x*1.0+sqrt(d*d-y*y);
		} 
		if(flag){
			cout<<"Case "<<c<<": "<<-1<<endl;
			cin>>n>>d;
			continue; 
		}
		sort(pos+1,pos+n+1);
		int last=0,ans=0;
		pos[last].f=-1e9;
		for(int i=1;i<=n;i++){
			if(pos[i].s>pos[last].f){
				++ans;
				last=i;
			}
		}
		cout<<"Case "<<c<<": "<<ans<<endl;
		cin>>n>>d;
	}
    return 0;
}

进阶题2

这题题目明确给出了不相交区间,我们不难想到模版题。但是这道题需要转一下弯:这里的区间是怎么构成的呢?

拿数据举例:

bash 复制代码
4 2
2 1 0 3

找权值为2的区间,我们不难想到前缀和的思想。开一个数组s,s[t]存储a[1]^ a[2] ^ ... ^ a[t]的结果。找权值为2的区间[l,r]就是找s[l-1]^s[r]=2的l,r。

why? 因为同一个数异或偶数次,结果为0,s[r]的计算过程中有s[l-1]的计算,再异或一次s[l-1],就抵消了a[1] ^ ... ^ a[l-1]的结果。s[l-1] ^ s[r]求到的就是a[l]^ ... ^a[r]的结果。

那怎么找满足s[l-1]^s[r]=k的区间[l,r]呢?我们按下标顺序枚举左端点,计算每个左端点找到与之匹配的右端点的条件:s[r]=s[l-1] ^ k。

我们一步步拆解来说:
1:s[r]=s[l-1] ^ k是什么?

要找s[l-1] ^ s[r]=k,等号两边同时异或s[l-1],得到的就是s[r]=k^s[l-1],我们枚举l,s[l-1]已知,k已知,就看如果s[r]=k ^ s[l-1]说明[l,r]权值为k。

2:如何使用选择尽可能多不相交的区间?

a数组下标 0 1 2 3 4
元素值 0 2 1 0 3
s数组下标 0 1 2 3 4
元素值 0 2 3 3 0

我们另开一个数组h,初始化h数组元素为-1。当s[l]^k=i时,h[i]=l。这样当找到s[r]=i时,我们就可以通过数组h找到l,区间[l+1,r]就是一个满足权值为k的区间。另外,我们开个变量last(初始化为-1)记录上一个满足条件的区间右端点的值。如果当前区间满足条件,且左端点比上一个满足条件的区间的右端点要大说明两个区间不相交,答案+1。

举例来说,变量i遍历s数组,
· i=0,s[i]=0,h[0]=-1,说明暂时没有匹配到满足的左端点,不处理。然后记录h[2]=0;
· i=1,s[1]=2,h[2]=0,匹配到了左端点为h[2]+1,然后看一下左端点的值是否大于last,是则ans++,last=i。然后记录h[0]=1;

h数组下标 0 1 2 3 4
元素值 1 -1 0 -1 -1

· i=2,s[i]=3,h[3]=-1,说明暂时没有匹配到满足的左端点,不处理。然后记录h[1]=2;
· i=3,s[i]=3,h[3]=-1,说明暂时没有匹配到满足的左端点,不处理。然后记录h[1]=3;

h数组:

h数组下标 0 1 2 3 4
元素值 1 3 0 -1 -1

· i=4,s[i]=0,h[0]=1,匹配到了左端点为h[0]+1,大于last,ans++,last=i。于是找到了两个不相交的区间。记录h[2]=4。

h数组:

h数组下标 0 1 2 3 4
元素值 1 3 4 -1 -1

3:在代码实现上要注意: a<=2^20,s数组要开到2e6比较保险。

AC代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
int n,k,a[maxn],s[maxn],h[maxn]; 
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++) {
		cin>>a[i];
		s[i]=s[i-1]^a[i];
	}
	for(int i=0;i<=n;i++)h[i]=-1;
	h[0^k]=1;
	int last=0,ans=0;
	for(int i=1;i<=n;i++){
		if(h[s[i]]!=-1&&h[s[i]]>last) {
			++ans;
			last=i;
		}
		h[s[i]^k]=i+1;
	}
	cout<<ans;
    return 0;
}
相关推荐
章小幽2 小时前
LeetCode-35.搜索插入位置
数据结构·算法·leetcode
放下华子我只抽RuiKe52 小时前
机器学习全景指南-探索篇——发现数据内在结构的聚类算法
人工智能·深度学习·算法·机器学习·语言模型·数据挖掘·聚类
Yupureki2 小时前
《C++实战项目-高并发内存池》3.ThreadCache构造
服务器·c语言·c++·算法·哈希算法
j_xxx404_2 小时前
C++算法:一维/二维前缀和算法模板题
开发语言·数据结构·c++·算法
x_xbx2 小时前
LeetCode:111. 二叉树的最小深度
算法·leetcode·职场和发展
入目星河滚烫2 小时前
网易互娱2020校招在线笔试—游戏研发第一批—游泳池-研发
算法·笔试·数据结构与算法
xier_ran2 小时前
【第一周】关键词解释:倒数排名融合(Reciprocal Rank Fusion, RRF)算法
开发语言·python·算法
spiritualfood2 小时前
蓝桥杯大学b组水质检测
c语言·c++·算法·青少年编程·职场和发展·蓝桥杯
进击的小头3 小时前
第6篇:贝尔曼最优化理论
python·算法·动态规划