AcWing 905,908,906,907(经典区间问题 贪心)

AcWing 905.区间选点、908.最大不相交区间数量

905 区间选点
给定 N 个闭区间 [ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。

输出选择的点的最小数量。

位于区间端点上的点也算作区间内。

输入格式
第一行包含整数 N,表示区间数。

接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。

输出格式
输出一个整数,表示所需的点的最小数量。

数据范围
1≤N≤10 ^5,
−10 ^9≤ai≤bi≤10 ^9
输入样例:
3
-1 1
2 4
3 5
输出样例:
2

贪心策略:

1.将每个区间按右端点从小到大排序

2.从前往后依次枚举每个区间

如果当前区间中已经包含点,则直接pass
否则,选择当前区间的右端点

#include<bits/stdc++.h>

using namespace std;

int n;
int a, b;
const int N = 1e5 + 10;
struct range
{
    int l,r;
    bool operator< (const range& x)const
    {
        return r<x.r;
    }
}Range[N];

int main()
{
    cin >> n;
    for (int i = 0; i < n; ++i)
    {
        scanf("%d%d", &a, &b);
        Range[i] = { a,b };
    }
    sort(Range, Range + n);//①将每个区间的 右端点 从小到大 排序
    int res = 0, end = -2e9;
    for (int i = 0; i < n; ++i)//②从前往后枚举每个区间
    {
        if (Range[i].l <= end) continue;//如果当前区间已经包含点而直接pass
        else//否则,
        {
            res++;
            end = Range[i].r;//选择当前区间的右端点
        }
    }
    cout << res << endl;
    return 0;
}

908 最大不相交区间数量
给定 N 个闭区间 [ai,bi],请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。

输出可选取区间的最大数量。

输入格式
第一行包含整数 N,表示区间数。

接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。

输出格式
输出一个整数,表示可选取区间的最大数量。

数据范围
1≤N≤10 ^5,
−10 ^9≤ai≤bi≤10 ^9
输入样例:
3
-1 1
2 4
3 5
输出样例:
2

#include<bits/stdc++.h>

using namespace std;

int n;
int a, b;
const int N = 1e5 + 10;
struct range
{
    int l,r;
    bool operator< (const range& x)const
    {
        return r<x.r;
    }
}Range[N];

int main()
{
    cin >> n;
    for (int i = 0; i < n; ++i)
    {
        scanf("%d%d", &a, &b);
        Range[i] = { a,b };
    }
    sort(Range, Range + n);//①将每个区间的 右端点 从小到大 排序
    int res = 0, end = -2e9;
    for (int i = 0; i < n; ++i)//②从前往后枚举每个区间
    {
        if (Range[i].l <= end) continue;//如果当前区间已经包含点而直接pass
        else//否则,
        {
            res++;
            end = Range[i].r;//选择当前区间的右端点
        }
    }
    cout << res << endl;
    return 0;
}

AcWing 906. 区间分组

给定 N 个闭区间 [ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。

输出最小组数。

输入格式
第一行包含整数 N,表示区间数。

接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。

输出格式
输出一个整数,表示最小组数。

数据范围
1≤N≤10 ^5,
−10 ^9≤ai≤bi≤10 ^9
输入样例:
3
-1 1
2 4
3 5
输出样例:
2

贪心策略:

1.将所有区间按左端点从小到大排序

2.从前往后处理每个区间,判断能否将其放到某个现有的组中,如果L[i] > Max_r,则可放入现有组

1.如果不存在这样的组,则开新组,然后再将其放进去
2.如果存在这样的组,将其放进去,并更新当前组的Max_r

注意

当判断能否将其放到某个现有的组中时,只需判断存在即可,因此我们用小根堆来实现这一点,堆顶存储的是 每个组最大右端点所形成的的集合中最小值

代码

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5+10;
int n;
struct range
{
    int a, b;
    bool operator< (const range& x)const
    {
        return a < x.a;
    }
}Range[N];

int main()
{
    cin>>n;
    for(int i=0; i<n; ++i) scanf("%d%d", &Range[i].a, &Range[i].b);
    
    sort(Range, Range+n);
    priority_queue<int, vector<int>, greater<int>> heap;
    
    for(int i=0; i<n; ++i)
    {
        int l = Range[i].a, r = Range[i].b;
        int top = !heap.empty() ? heap.top() : -2e9;//如果堆为空就取出堆顶,会导致 segment fault,因此要写成这样
        if(!heap.size()||l<=top) heap.push(r);
        else
        {
            heap.pop();
            heap.push(r);
        }
    }
    
    cout<<heap.size()<<'\n';
    
    return 0;
}

代码 + 注释

#include<bits/stdc++.h>

using namespace std;

int n, a, b;
const int N = 1e5 + 10;
struct range
{
    int l, r;
    bool operator< (const range& x) const
    {
        return l < x.l;
    }
}Range[N];

int main()
{
    cin >> n;
    for (int i = 0; i < n; ++i)
    {
        scanf("%d%d", &a, &b);
        Range[i] = { a,b };
    }
    sort(Range, Range + n);//①将所有区间按照左端点由小到大排序
    priority_queue<int, vector<int>, greater<int>> pq;
    //小根堆pq存储每个组中所有区间右端点的最大值max_r,其size()为分配的组数,堆顶为所有组中max_r的最小值
    for (int i = 0; i < n; ++i)//②从前往后处理每一个区间判断是否能将其放到某个现有的组中
    {
        if (pq.empty() || Range[i].l <= pq.top())// 如果堆为空或者堆的最小右端点 >= 现在区间的左端点,则说明有交集,不能合并,需要新开一个堆
        {
            pq.push(Range[i].r);
        }
        else// 否则说明至少与最小的右端点的组没有交集,将它放到右端点最小的组里去,并更新当前组的max_r
        {
            //弹出压入保证pq.size()不变
            pq.pop();// 弹出这个区间的最小右端点,插入当前区间的右端点,即将其更新了
            pq.push(Range[i].r);
        }
    }
    cout << pq.size() << endl;
    return 0;
}

AcWing 907. 区间覆盖

给定 N 个闭区间 [ai,bi] 以及一个线段区间 [s,t],请你选择尽量少的区间,将指定线段区间完全覆盖。

输出最少区间数,如果无法完全覆盖则输出 −1。

输入格式
第一行包含两个整数 s 和 t,表示给定线段区间的两个端点。

第二行包含整数 N,表示给定区间数。

接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。

输出格式
输出一个整数,表示所需最少区间数。

如果无解,则输出 −1。

数据范围
1≤N≤10 ^5,
−10 ^9≤ai≤bi≤10 ^9,
−10 ^9≤s≤t≤10 ^9
输入样例:
1 5
3
-1 3
2 4
3 5
输出样例:
2

贪心策略:

1.将所有区间按左端点从小到大排序

2.从前往后依次枚举每个区间,在所有能覆盖 start 的区间中,选择右端点最大的区间然后将 start 更新成右端点的最大值

#include<bits/stdc++.h>

using namespace std;

int n, a, b, Start, End;
const int N = 1e5 + 10;
struct range
{
	int l, r;
	bool operator< (const range& x) const
	{
		return l < x.l;
	}
}Range[N];

int main()
{
	cin >> Start >> End;
	cin >> n;
	for (int i = 0; i < n; ++i)
	{
		scanf("%d%d", &a, &b);
		Range[i] = { a,b };
	}
	sort(Range, Range + n);//①将所有区间按左端点由小到大排序
	int res = 0;
 	bool ok = false;//判断是否有方案
	for (int i = 0; i < n; ++i)//②从前往后依次枚举每个区间,在所有能覆盖Start的区间中,选择左端点最大的区间然后将Start更新成右端点的最大值
	{
		int j = i;
		int ans = -2e9;//
		//遍历所有左端点在Start左边的所有区间右端点的最大值是多少
		while (j < n && Range[j].l <= Start)
		{
		    //先max后j+1
		    ans = max(ans, Range[j].r);
			++j;
		}
		if (ans < Start)//如果最大值都小于start则说明无解,将res赋为-1并break
		{
			res = -1;
			break;
		}
		++res;
		if (ans >= End)//如果最大值已经大于等于End,则表明已经能够完全覆盖给定区间了,则可以ok更新为true并break,不要忘了'=',在while之前还要使答案res+1
		{
 			ok = true;
			break;
		}
		Start = ans;//将Start更新为当前的最大值
		i = j - 1;
	}
 	if (ok == false) res = -1;
	cout << res << endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值