算法笔记 4.4 贪心 区间贪心

1.简单贪心

PAT B1020 月饼
PAT B1023 组个最小数

2.区间贪心

给出N个开区间(x,y),从中选择尽可能多的开区间,使得这些开区间两两之间没有交集

例如:(1,3)、(2,4)、(3,5)、(6,7) 最多选3个:(1,3),(3,5)、(6,7)

贪心算法思想:

基础两点:

a.优先选择短的区间
b.如果存在一个区间包含另一个区间,应选择更小的区间. 


算法(左端点为例)

1、对区间左端点进行从大到小排序,左端点相同按右端点从小到大排序 (右端点排序遵循b原则)
2、依次选出左端点最大的区间,当待选择区间与已选区间集合相交时,舍弃待选区间
3、首先:排好后的第一个不是原始序列的第一个。其次:每次直接从排好后的第一个开始选,也是为了使左边预留区间最大

代码:

#include<iostream>		 
using namespace std;

struct Inteval
{
	int x,y;//开区间左右端点
}I[1000];

bool cmp(Inteval a,Inteval b){
	if(a.x!=b.x) return a.x>b.x;//左端点从大到小排序
	else return a.y<b.y; //左端点相同 按右端点从小到大排序 即左端点相同,优先选择短的
}

int main(){
	int n;//先输入区间个数
	while(cin>>n){
		for(int i=0;i<n;i++){
			cin>>I[i].x>>I[i].y;
		}
		sort(I,I+n,cmp);
		int ans=1,lastx=I[0].x;//选中x最大的第一个
		cout<<"选择以下区间:"<<endl;
		cout<<"("<<I[0].x<<","<<I[0].y<<")"<<endl;
		for(int i=1;i<n;i++){
			if(I[i].y<=lastx){//不重叠  闭区间 小于等于
				lastx=I[i].x;
				ans++;
				cout<<"("<<I[i].x<<","<<I[i].y<<")"<<endl;
			}
		}
		cout<<"共计"<<ans<<"个区间!"<<endl;//最多个数
	}

	return 0;
}


lx:区间选点问题:

给出N个闭区间[x,y],求最少需要确定多少个点,才能使每个闭区间都至少存在一个点。例如:对闭区间[1,4]、[2,6]、[5,7]来说,需要两个点(例如3,5)。

分析:将上面不重叠区间数最多问题的I[i].y<=lastx改成I[i].y<lastx即可,原理如下

先按左区间从大到小排好序,每个区间内都要至少包含一个点,对于左区间值最大的区间I1来说,选哪个点可以尽可能多的覆盖其他区间呢?很显然,取左端点值,因为已经排好序了,越往左,覆盖的区间数越多。每次选的都是lastx,I[i].y=lastx的区间也被覆盖了,因此将上述代码I[i].y<=lastx改成I[i].y<lastx即可。

代码:

#include<iostream>		 
using namespace std;

struct Inteval
{
	int x,y;//开区间左右端点
}I[1000];

bool cmp(Inteval a,Inteval b){
	if(a.x!=b.x) return a.x>b.x;//左端点从大到小排序
	else return a.y<b.y; //左端点相同 按右端点从小到大排序 即左端点相同,优先选择短的
}

int main(){
	int n;//先输入区间个数
	while(cin>>n){
		for(int i=0;i<n;i++){
			cin>>I[i].x>>I[i].y;
		}
		sort(I,I+n,cmp);
		int ans=1,lastx=I[0].x;//选中x最大的第一个
		cout<<"选择以下点:"<<endl;
		cout<<lastx;
		for(int i=1;i<n;i++){
			if(I[i].y<lastx){//不重叠  闭区间 小于等于
				lastx=I[i].x;
				ans++;
				cout<<" "<<lastx;
			}
		}
		cout<<"\n共计需要"<<ans<<"个点!"<<endl;//最多个数
	}

	return 0;
}


猜你喜欢

转载自blog.csdn.net/hza419763578/article/details/82952136
今日推荐