【贪心算法(二)】三类区间问题

本文解析了贪心算法中的三类区间问题:互不相交的会议室问题、区间覆盖问题及区间选点的海岸线问题,并提供了源码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


1介绍

本节记录了贪心算法的三类区间问题:互不相交,区间覆盖,区间选点。按照自己的思路进行了一定的解析。较为简单,不说废话了。

2互不相交_会议室问题

2.1解析

问题:有n个需要在同一天使用同一个教室的活动a1,a2,…,an,教室同一时刻只能由一个活动使用。每个活动ai都有一个开始时间si和结束时间fi 。一旦被选择后,活动ai就占据半开时间区间[si,fi)。如果[si,fi]和[sj,fj]互不重叠,ai和aj两个活动就可以被安排在这一天。

该问题就是要安排这些活动使得尽量多的活动能不冲突的举行。

(1)      依据贪心思想,每次选取最优结果,倘若每次从未举办的活动中选取活动结束时间最早的活动来举办,是否意味着在24内就能尽可能多的举办活动呢?

(2)      将数组按结束时间排序,在选择时还要确保下次开始时间大于等于本次结束时间

2.2源码

#include<iostream>
using namespace std;
#define Max 10//最多活动个数  
int main(){
	int i,j,temp;//循环用
	int n;//活动个数
	int arry[Max][2];
	int sum;//活动个数
	int s=0,e=24;//一天24个小时【0,24】
	cin>>n;
	for(i=0;i<n;i++){
		cin>>arry[i][0]>>arry[i][1];
	}
	//冒泡排序:升序
	//按结束时间排序
	for (i = 0; i < n; i++){  
        for (j = i + 1; j < n; j++){  
            if (arry[i][1]> arry[j][1]){  
                temp=arry[i][1];
				arry[i][1]=arry[j][1];
				arry[j][1]=temp;

				//整组交换
				temp=arry[i][0];
				arry[i][0]=arry[j][0];
				arry[j][0]=temp;
            }  
        }  
    }  
	//初始化赋值
	temp=arry[0][1];
	cout<<"["<<arry[0][0]<<","<<arry[0][1]<<")"<<endl;
	sum=1;
	for(i=1;i<n;i++){
		if(temp<=arry[i][0]){
			sum++;
			temp=arry[i][1];
			cout<<"["<<arry[i][0]<<","<<arry[i][1]<<")"<<endl;
		}
		//应该判断一下合法性,但是怕影响思路,还是不要了
		//if(arry[i][1]>24 || arry[i][0]>24)
			//break;
	}
	cout<<sum;
	return 0;
}

2.3结果

测试用例:5  ;

【2,10)【5,6)【11,13)【13,20)【19,24);

3区间覆盖

3.1解析

问题:给定一个长度为m的区间,再给出n条线段的起点和终点(注意这里是闭区间),

求最少使用多少条线段可以将整个区间完全覆盖

(1)      既然是完全覆盖,那么必定不存在相邻区间不相交的情况,也就是相邻区间必定是相交的。比如:【1,3】【4,5】【5,6】三条区间是不可能覆盖区间【1,6】

(2)      这里所说的相邻区间指的是按左端点排序以后的各个区间。比如【1,4】【5,6】【3,5】,不能说【1,4】与【5,6】是相邻区间不相交的情况,因为先排序得到【1,4】【3,5】【5,6】所以该组三个区间不存在相邻区间不相交的情况。

(3)      比较麻烦的是下面这种情况,你需要在【3,6】【2,7】之间做一个选择:


(4)      因为要选择最少的区间个数,所以应该选【2,7】。如何选择【3,6】【2,7】?循环与【1,4】相交的区间从中找出右端点最大的即可,这也是这段程序复杂的地方。

3.2源码

#include<iostream>
using namespace std;
#define Max 10
int main(){
	int i,j,temp,k;//循环用
	int n;
	int m;
	int arry[Max][2];
	int sum;//总数
	int start,end;
	cout<<"总区间长度:";cin>>m;
	cout<<"线段条数:";cin>>n;
	for(i=0;i<n;i++){
		cin>>arry[i][0]>>arry[i][1];
	}
	//冒泡排序:升序
	//左端点从小到大排
	for (i = 0; i < n; i++){  
        for (j = i + 1; j < n; j++){  
            if (arry[i][0]> arry[j][0]){ 
				temp=arry[i][0];
				arry[i][0]=arry[j][0];
				arry[j][0]=temp;
				//整组交换
				 temp=arry[i][1];
				arry[i][1]=arry[j][1];
				arry[j][1]=temp;
            }  
        }  
    }  
	//主要的逻辑计算
	//第一个必选
	sum=1;
	i=0;
	start=arry[0][0];
	end=arry[0][1];
	cout<<"("<<start<<","<<end<<")"<<endl;
	//主要
	int endM=end;//处理多区间相交时,取最大区间的问题
	while(endM<8){
		for(j=i+1;j<n;j++){
			if(arry[j][1]>end && arry[j][0]<=end && arry[j][1]>=endM){//相交
				endM=arry[j][1];
				i=j;
			}
			else{
				break;
			}
		}
		sum++;
		end=arry[i][1];//更新end的数值
		cout<<"("<<arry[i][0]<<","<<arry[i][1]<<")"<<endl;
	}
	cout<<"组数:"<<sum;
	return 0;
}

3.3结果


测试用例A:

区间长度:8也就是【1,8】;可选区间条数:5;

区间:【1,4】【2,5】【3,5】【3,7】【6,8】


4区间选点_海岸线问题

4.1解析

问题:假设海岸线是一条无限延伸的直线。陆地在海岸线的一侧,而海洋在另一侧。
每一个小的岛屿是海洋上的一个点。雷达坐落于海岸线上,只能覆盖d距离。
题目要求计算出能够覆盖给出的所有岛屿的最少雷达数目。
(1) 通过下列图来分析,更易理解
(2) 至少需要两个雷达,才能覆盖A,B岛

(3)      至少需要1个雷达,才能覆盖A,B岛


(4)      至少需要1个雷达,才能覆盖A,B岛


4.2源码

D是用来确定该岛屿能被雷达扫射到的区间,此处直接给出该区间,免去数学计算。

#include<iostream>
using namespace std;
#define Max 10
int main(){
	int i, j, temp;//循环用
	int n,sum;
	int arry[Max][2];
	int start, end;//区间开始的点和区间结束的点
	cin >> n;
	for (i = 0; i < n; i++){
		cin >> arry[i][0] >> arry[i][1];
	}
	//冒泡排序:升序
	//按左点从小到大排序
	for (i = 0; i < n; i++){
		for (j = i + 1; j < n; j++){
			if (arry[i][0]> arry[j][0]){
				temp = arry[i][0];
				arry[i][0] = arry[j][0];
				arry[j][0] = temp;
				//整组交换
				temp = arry[i][1];
				arry[i][1] = arry[j][1];
				arry[j][1] = temp;
			}
		}
	}
	//分析
	start = arry[0][0];
	end = arry[0][1];
	sum = 1;//sum是雷达个数,最少一个
	for (i = 1; i < n;i++){
		if (start<=arry[i][0] &&end>=arry[i][1]){//区间包含
			start = arry[i][0]; end=arry[i][1];
		}
		else if (end>arry[i][0] && end<=arry[i][1]){//区间相交
			start = arry[i][0];
		}
		else if (end<=arry[i][0]){//区间交集为空
			cout << "(" << start << "," << end << ")" << endl;//输出,可以不看
			sum++;
			start = arry[i][0];
			end = arry[i][1];
		}
	}
	cout << "(" << start << "," << end << ")" << endl;//输出,可以不看
	cout << "最少雷达=" << sum;
	return 0;
}

4.3结果

测试用例A:5  ;

(2,5)(6,9)(8,11)(10,11)(9,15)




测试用例B:4 ;

(1,5)(2,7)(3,5)(6,10)



5总结

比较有疑虑的地方在于,没有找到对应题目所在的网址,也就是代码没有AC过,所以我尽量编写测试用例来测试代码的准确性,目前没有发现不合理的地方。一方面要照顾输出的格式,另一方面也是为了方便读者理解,代码有一些可以优化和简洁的地方就没有做了。


### 贪心算法解决区间调度问题 #### 示例说明 在一个典型的时间区间调度问题中,假设有一组活动,每个活动一个开始时间和结束时间。目标是从这些活动中选择尽可能多的互不重叠的活动[^5]。 为了实现这一目标,可以采用贪心算法的思想:每次都优先选择结束时间最早的活动,并移除与其冲突的所有其他活动。这种策略能够保证在每一步都做出局部最优的选择,从而最终获得全局次优或最优解[^1]。 #### 实现步骤解析 以下是基于上述思想的具体实现方法: 1. **输入数据结构化** 输入的数据应表示为一系列区间 `[start, end]`,其中 `start` 表示活动的起始时间,`end` 表示活动的终止时间。 2. **按结束时间排序** 对所有的活动按照其结束时间从小到大进行排序。如果两个活动具有相同的结束时间,则可以根据它们的开始时间进一步排序[^4]。 3. **迭代选取活动** 初始化一个变量记录上一次选中的活动的结束时间。遍历已排序的活动列表,在每次循环中判断当前活动是否与前一选定活动存在冲突(即当前活动的开始时间小于等于上次选中活动的结束时间)。如果没有冲突,则将其加入结果集中并更新记录的结束时间为当前活动的结束时间。 #### Java代码实现 下面提供了一个完整的Java程序来展示如何利用贪心算法求解此类问题: ```java import java.util.*; public class IntervalScheduling { public static List<int[]> selectMaxNonOverlappingIntervals(List<int[]> intervals) { if (intervals == null || intervals.isEmpty()) return new ArrayList<>(); // Sort the intervals by their ending times. Collections.sort(intervals, Comparator.comparingInt(interval -> interval[1])); List<int[]> result = new ArrayList<>(); int[] lastSelectedInterval = intervals.get(0); result.add(lastSelectedInterval); for (int i = 1; i < intervals.size(); ++i){ int[] currentInterval = intervals.get(i); if(currentInterval[0] >= lastSelectedInterval[1]){ result.add(currentInterval); lastSelectedInterval = currentInterval; } } return result; } public static void main(String[] args) { List<int[]> activities = Arrays.asList( new int[]{1, 3}, new int[]{2, 4}, new int[]{3, 5}, new int[]{6, 8} ); List<int[]> selectedActivities = selectMaxNonOverlappingIntervals(activities); System.out.println("Selected Activities:"); for (int[] activity : selectedActivities) { System.out.printf("[%d, %d]%n", activity[0], activity[1]); } } } ``` 此代码实现了基本的功能需求,展示了如何通过排序和简单的逻辑操作快速筛选出符合条件的最大数量无交叉区间集合[^3]。 #### 结果分析 运行以上代码后,可以看到输出选择了哪些活动作为解决方案的一部分。这种方法不仅简单高效而且易于理解,非常适合用于实际应用场景下的初步规划阶段[^2]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值