当前位置: 代码网 > it编程>编程语言>C/C++ > 【C++算法】贪心算法-区间问题,超详细注释例题讲解

【C++算法】贪心算法-区间问题,超详细注释例题讲解

2024年07月28日 C/C++ 我要评论
贪心策略:新建一个结构体存储区间的左端点和右端点,将这N个区间按照右端点从小到大排序,遍历所有的区间,如果当前遍历到的区间左端点的值大于了上一个区间的右端点的值,说明此时区间没有重合,则区间个数+1

1)最大不相交区间数(区间选点)

在这里插入图片描述

  • 翻译一下题目,也就是说给定 n n n个闭区间,问能从中选出多少个区间使其互不相交
  • 贪心策略:新建一个结构体存储区间的左端点和右端点,将这 n n n个区间按照右端点从小到大排序,遍历所有的区间,如果当前遍历到的区间左端点的值大于了上一个区间的右端点的值,说明此时区间没有重合,则区间个数 + 1 +1 +1
#include<bits/stdc++.h>
#define x first
#define y second

using namespace std;

typedef long long ll;
typedef pair<int,int> pii;

// 解题思路: 

const int n=1e5+5; // 区间总数

// 区间
struct range {
	int l; // 左端点
	int r; // 右端点
}range[n];

// 按照区间右端点从小到大排序
bool cmp(range a,range b) {
	return a.r<b.r;
}

int n;

int main() {
	cin>>n;
	// 输入规模大于1e5,推荐使用速度更快的scanf
	for(int i=1;i<=n;i++) {
		scanf("%d%d",&range[i].l,&range[i].r);
	}
	// 计算答案
	int res=0;
	int front_r=int_min; // i=1时至少有一个,所以res从0开始
	sort(range+1,range+1+n,cmp); // 排序
	for(int i=1;i<=n;i++) {
		if(range[i].l>front_r) {
			res++;
			front_r=range[i].r; // 更新上一个区间的右端点
		}
	}
	cout<<res;
	return 0;
}

2)区间分组

在这里插入图片描述

  • 翻译一下题目,现在有 n n n个区间,问最少分多少组,能让这些分组中的所有区间两两之间包括端点都没有交集
  • 贪心策略:将所有区间按照左端点从小到大排序,从前往后处理每个区间,判断是否能把这个区间放到现有的某个分组中,即判断 r a n g e [ i ] . l   >  某一分组最大右端点 range[i].l \ > \ 某一分组最大右端点 range[i].l > 某一分组最大右端点
    1. 如果存在这样的分组(与某个分组间没有交集),则将区间 $ i $ 放进去,并且更新这个分组的最大右端点(注意,如果与多个分组都有交集则可以放到任意一个分组中,不影响结果,可自行模拟证明)
    2. 如果不存在这样的分组(与每个分组间都有交集),则开一个新组,再把区间 i i i 放进去
    3. 在判断当前区间是否与某一分组有交集时,只需要与所有分组中最小的分组最大右端点进行比较即可,因为这是最有可能满足 r a n g e [ i ] . l   >  某一分组最大右端点 range[i].l \ > \ 某一分组最大右端点 range[i].l > 某一分组最大右端点 的情况,对于所有分组中最小的分组最大右端点,只需要用一个小根堆来维护即可
#include<bits/stdc++.h>
#define x first
#define y second

using namespace std;

typedef long long ll;
typedef pair<int,int> pii;

// 解题思路: 
// 将所有区间按照左端点从小到大排序,从前往后遍历每个区间,判断能否将其放进某个现有的组中
// 用堆来维护每个分组的max_r,

const int n=1e5+5;

struct range {
	int l;
	int r;
}range[n];

bool cmp(range a,range b) {
	// 按照左端点从小到大排序
	return a.l<b.l;
}

int main() {
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) {
		int l,r;
		scanf("%d%d",&l,&r);
		range[i]={l,r};
	}
	sort(range+1,range+1+n,cmp);
	priority_queue<int,vector<int>,greater<int>>heap; // greater升序原则小根堆,维护每个分组的max_r的最小值
	// 枚举每个区间
	for(int i=1;i<=n;i++) {
		// 如果每个分组中最小的max_r都比当前区间的左端点大(说明有交集,不能放入该区间中)
		if(heap.empty() || heap.top()>=range[i].l) {
			heap.push(range[i].r); // 新开一个分组
		} else {
			// 如果无交集,说明可以放入该分组中,此时弹出再更新这个分组的最大值
			heap.pop(); // 因为rnage[i].r一定比heap.top()更大,不然怎么可能无交集
			heap.push(range[i].r);
		}
	}
	// 小根堆的大小就是分组的个数
	cout<<heap.size()<<endl;
	return 0;
}

3)区间覆盖

在这里插入图片描述

  • 翻译一下题目,···,这道题没什么可翻译的
  • 贪心策略:将所有区间按照左端点从小到大排序,从前往后依次枚举每个区间,在所有能覆盖 s t a r t start start 的区间中选择一个右端点最大的区间,然后将 s t a r t start start 更新成为右端点的最大值,继续向后覆盖,如果所有区间都遍历过了但并没有覆盖到 e n d end end 的话说明无法完全覆盖,则输出 − 1 -1 1
#include<bits/stdc++.h>
#define x first
#define y second

using namespace std;

typedef long long ll;
typedef pair<int,int> pii;

// 解题思路: 双指针+贪心

const int n=1e5+5;

struct range {
   int l,r; // 左端点,右端点
   // 重载运算符从小到大排序
   bool operator<(const range &w)const {
   	return l<w.l;
   }
}range[n];

int main() {
   int st,en;
   cin>>st>>en;
   int n;
   cin>>n;
   for(int i=1;i<=n;i++) {
   	int l,r;
   	scanf("%d%d",&l,&r);
   	range[i]={l,r};
   }
   sort(range+1,range+1+n);
   int res=0; // 存储答案
   bool success=false;
   // 遍历所有区间
   for(int i=1;i<=n;i++) {
   	// front_r可理解为当前覆盖到的区域
   	int j=i,front_r=int_min; // 双指针,j从第i个区间开始找
   	// 取所有左端点能覆盖st中右端点最大的区间,覆盖到这个位置
   	while(j<=n && range[j].l<=st) {
   		front_r=max(front_r,range[j].r);
   		j++;
   	}
   	// 如果front_r仍小于st,说明线段不可被已有区间覆盖
   	if(front_r<st) {
   		break;
   	}
   	// 如果找得到,答案+1
   	res++;
   	// 已经完全覆盖
   	if(front_r>=en) {
   		success=true;
   		break;
   	}
   	// 否则更新i和j
   	st=front_r; // 迭代st,后面的区间继续和st比
   	i=j-1; // 双指针,加速遍历
   }
   if(!success) res=-1;
   cout<<res;
   return 0;
}
(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com