WEEK3 周记 作业B题——贪心算法_区间选点

WEEK3 周记 作业B题——贪心算法_区间选点

一、题意

1.简述

数轴上有 n 个闭区间 [a_i, b_i]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)

2.输入格式

第一行1个整数N(N<=100)
第2~N+1行,每行两个整数a,b(a,b<=100)

3.输出格式

一个整数,代表选点的数目

样例1

Input

2
1 5
4 6

Output

1

样例2

Input

3
1 3
2 5
4 6

Output

2

二、算法

1.主要步骤

第一种贪心的思路,也是网上的思路,比较好写。思路如下:
我们贪心的认为选的点一定是某区间的右端点。我们事先根据右端点对区间进行排序(我这里将区间加入了小根堆)。取出一个区间,初始区间的右端被选定了,下面我们判断这个右端点能往后覆盖多少个区间。我们继续从小根堆中取出一个区间,如果这个区间的左端点 a a a要小于等于我们选定的右端点 b b b,那么这个区间就被覆盖了。否则 b b b就无法继续往后覆盖区间了(可能还可以,但是没必要了。比如第二个区间没法覆盖,有可能第三个区间能覆盖,但是第二个区间迟早要被选定右端点,那么第二个区间就能覆盖第三个区间,所以没必要了),被换为新取出的这个区间的右端点。依次类推,直到小根堆空。

一定要注意是 a ≤ b a\le b ab而不是 a < b a\lt b a<b,因为是闭区间,一个点重合也是重合!

下面附上代码:

struct item{//存储区间
	int a;
	int b;
	bool operator<(const item& i)const
	{//两个const必须加,这是priority_queue重载的<函数的原因 
		return b>i.b;//我们想要小根堆 
	}
	item(int a,int b):a(a),b(b){}
	bool cover(int aa)
	{//传过来某一个区间的左端点,返回是否能覆盖这个区间
		return b>=aa;
	}
};
while(!q.empty())
{
	item i1=q.top();
	q.pop();
	if(q.empty()) break;
	item i2=q.top();
	if(i1.cover(i2.a))
	{
		q.pop();
		q.push(i1);
	}
	else cnt++;
}

//或者(没用到cover函数)
//与上面的稍有区别,思路是完全一样的
int deadline;
	if(!q.empty()) 
	{
		deadline=q.top().b;
		q.pop();
		cnt++;
	}
	while(!q.empty())
	{	
		item i2=q.top();
		q.pop();
		if(deadline<i2.a)
		{
			deadline=i2.b;
			cnt++;
		}
	}

我自己写的贪心思路是这样的:

首先提出一个问题,三个区间什么时候能够只取一个点就够了?
假设有 A : [ 1 , 5 ] A:[1,5] A:[1,5] B : [ 3 , 8 ] B:[3,8] B:[3,8] C : [ 4 , 6 ] C:[4,6] C:[4,6]三个区间,区间 A A A和区间 B B B合并之后,产生一个重合的小区间 [ 3 , 5 ] [3,5] [3,5],而事实上,只要这个小区间与区间 C C C有重合的点或区间,这三个区间就能只取一个点。

我将区间按照左端点的大小排序,将区间压入了小根堆。我一次循环 p o p pop pop出两个区间,用cover函数(定义在 i t e m item item结构体中的)检查第一个区间是否部分覆盖了第二个区间,如果覆盖了,那么必然存在一个重合的小区间 [ a 2 , b b ] [a_2,bb] [a2,bb],其中 a 2 a_2 a2是第二个区间的左端点,bb=(i1.b<i2.b)? i1.b:i2.b;即两个区间的右端点中的小的那一个。然后将这个小区间重新压入堆中。如果两个区间并不存在重合的部分,那么就将第二个区间重新压回堆中。这个时候被抛弃的第一个区间就必然要选定一个点了,cnt++

具体代码见第三节代码部分。

三、代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
struct item{
	int a;
	int b;
	bool operator<(const item& i)const
	{//两个const必须加,这是priority_queue重载的<函数的原因 
		return a>i.a;//我们想要小根堆 
	}
	item(int a,int b):a(a),b(b){}
	bool cover(int aa)
	{
		return b>=aa;//必须写等于号,我就是在这里错的
	}
};
int cnt;
int main()
{
	priority_queue<item> q;
	int n,i,j;
	scanf("%d",&n);
	for(i=0;i<n;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		if(a>b) continue;
		item t(a,b);
		q.push(t);
	}
	while(!q.empty())
	{
		item i1=q.top();
		q.pop();
		if(q.empty()) break;
		item i2=q.top();
		if(i1.cover(i2.a))
		{
			q.pop();
			int bb=(i1.b<i2.b)? i1.b:i2.b;
			q.push(item(i2.a,bb));
		}
		else cnt++;
	}
	if(n>0)
		cnt++;
	printf("%d",cnt);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值