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

参考链接

问题:跟定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,st存储a1^ a2 ^ ... ^ at的结果。找权值为2的区间l,r就是找sl-1^sr=2的l,r。

why? 因为同一个数异或偶数次,结果为0,sr的计算过程中有sl-1的计算,再异或一次sl-1,就抵消了a1 ^ ... ^ al-1的结果。sl-1 ^ sr求到的就是al^ ... ^ar的结果。

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

我们一步步拆解来说:
1:sr=sl-1 ^ k是什么?

要找sl-1 ^ sr=k,等号两边同时异或sl-1,得到的就是sr=k^sl-1,我们枚举l,sl-1已知,k已知,就看如果sr=k ^ sl-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。当sl^k=i时,hi=l。这样当找到sr=i时,我们就可以通过数组h找到l,区间l+1,r就是一个满足权值为k的区间。另外,我们开个变量last(初始化为-1)记录上一个满足条件的区间右端点的值。如果当前区间满足条件,且左端点比上一个满足条件的区间的右端点要大说明两个区间不相交,答案+1。

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

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

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

h数组:

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

· i=4,si=0,h0=1,匹配到了左端点为h0+1,大于last,ans++,last=i。于是找到了两个不相交的区间。记录h2=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;
}
相关推荐
To_OC6 小时前
LC 994 腐烂的橘子:人人都说是 BFS 入门题,我却写了三遍才过
javascript·算法·leetcode
金銀銅鐵10 小时前
[Python] 扩展欧几里得算法
python·数学·算法
To_OC12 小时前
LC 200 岛屿数量:经典 DFS 入门题,我第一次写居然连方向都搞错了
javascript·算法·leetcode
To_OC1 天前
LC 128 最长连续序列:别上来就排序,O (n) 解法才是这题的灵魂
javascript·算法·leetcode
刘马想放假2 天前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
05Kevin2 天前
lk每日冒险题--数据结构6.27
算法
To_OC2 天前
从一次栈溢出报错说起,我把递归彻底扒明白了
javascript·算法·程序员
千纸鹤安安2 天前
千问Qwen-AgentWorld来了:一个语言模型搞定七大Agent场景,GPT-5.4都输了
算法