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

参考链接

问题:跟定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;
}
相关推荐
huohaiyu29 分钟前
深入解析Java垃圾回收机制
java·开发语言·算法·gc
浮芷.36 分钟前
鸿蒙PC端 TTS 并发调用问题详解:资源竞争与队列管理
算法·华为·开源·harmonyos·鸿蒙·鸿蒙系统
装不满的克莱因瓶44 分钟前
掌握感知器的学习原理
人工智能·python·神经网络·算法·ai·卷积神经网络
Lsk_Smion44 分钟前
力扣实训 _ [994].腐烂的橘子/图论
算法·leetcode·图论
轻微的风格艾丝凡1 小时前
两电平三相VSC整流模式从不控整流平滑切换至有源整流调试记录
算法·dsp·c2000
dongf20191 小时前
R语言KNN算法
算法·数据分析·r语言
小O的算法实验室1 小时前
2025年IEEE TASE,基于双层耦合平均场博弈的大规模智能体集成任务分配与轨迹规划
人工智能·算法·机器学习
8Qi82 小时前
LeetCode 337:打家劫舍 III(House Robber III)—— 题解 ✅
算法·leetcode·二叉树·动态规划
地平线开发者2 小时前
从 INT64 Div 算子约束到 Cast 修复全流程
算法
AI科技星2 小时前
基于奇合数边界的离散解析数论与双螺旋宇宙本体大统一体系论文全部数学公式汇总表
人工智能·算法·机器学习·架构·学习方法