蓝桥杯算法精讲:贪心算法之区间问题深度剖析

目录

  • 前言
  • 一、贪心算法
    • [1.1 区间问题](#1.1 区间问题)
      • [1.1.1 线段覆盖](#1.1.1 线段覆盖)
      • [1.1.2 Radar Installation](#1.1.2 Radar Installation)
      • [1.1.3 Sunscreen](#1.1.3 Sunscreen)
      • [1.1.4 牛栏预定](#1.1.4 牛栏预定)
  • 结语

🎬 云泽Q个人主页
🔥 专栏传送入口 : 《C语言》《数据结构》《C++》《Linux》《蓝桥杯系列

⛺️遇见安然遇见你,不负代码不负卿~


前言

大家好啊,我是云泽Q,欢迎阅读我的文章,一名热爱计算机技术的在校大学生,喜欢在课余时间做一些计算机技术的总结性文章,希望我的文章能为你解答困惑~

一、贪心算法

1.1 区间问题

区间问题是另一种比较经典的贪心问题。题目面对的对象是一个一个的区间,让我们在每个区间上做出取舍。

这种题目的解决方式一般就是按照区间的左端点或者是右端点排序,然后在排序之后的区间上,根据题目要求,制定出相应的贪心策略,进而得到最优解。

具体是根据左端点还是右端点排序?升序还是降序?一般是假设一种排序方式,并且制定贪心策略,当没有明显的反例时,就可以尝试去写代码。

1.1.1 线段覆盖

凌乱的yyy / 线段覆盖


解法

按照区间左端点从小到大排序,当两个区间「重叠」的时候,我们必须要舍弃一个。为了能够「在移除某个区间后,保留更多的区间」,我们应该把「区间范围较大」的区间移除。

因此以第一个区间为基准,遍历所有的区间:

  • 如果重叠,选择「最小的右端点」作为新的基准;
  • 如果不重叠,那么我们就能多选一个区间,以「新区间为基准」继续向后遍历。
cpp 复制代码
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;
const int N = 1e6 + 10;
struct node{
	LL l;
	LL r;
}a[N];
LL n;

bool cmp(node& x, node& y)
{
	return x.l < y.l;
}

int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i].l >> a[i].r;
	sort(a + 1, a + 1 + n, cmp);
	//以第一个区间为右端点,第一个区间必选
	LL ret = 1;
	//以第一个区间的右端点为基准向后选择
	LL right_side = a[1].r;
	for(int i = 2; i <= n; i++)
	{
		LL left = a[i].l, right = a[i].r;
		//重叠 
		if(left < right_side)
		{
			//保留右端点较小的区间
			right_side = min(right_side, right);
		}else{//不重叠 
			ret++;
			right_side = right;
		}
	}
	cout << ret << endl;
	return 0;
}


1.1.2 Radar Installation

Radar Installation

如图所示,当一个岛屿的坐标已知,其实可以计算出:当雷达放在 x 轴的哪段区间时,可以覆盖到这个岛屿

根据勾股定理得:ax的长度 l=根号下d2−y2,那么雷达所处的范围就是 [x - l,x + l]。因此,针对每一个岛屿,我们都可以算出一个能够覆盖它的区间。

原问题就变成给定一些区间,从中选取一些区间(尽量多的重叠区间),能够覆盖掉所有的岛屿,最少能够选多少个。就从二维平面转化成一维线段

cpp 复制代码
#include<iostream>
#include<cmath>      //开根号用 
#include<algorithm>  //sqort
using namespace std;

//区间数/岛屿数 
const int N = 1010;
double d;
int n;

//存储每个岛屿对应的「雷达可覆盖区间」(x轴上的区间)
struct node{
	//计算的时候因为涉及开根号,所以这里的数据类型是double
    double l;
    double r;
}a[N];

// 排序比较函数:按区间左端点升序排序
bool cmp(node& x, node& y)
{
    return x.l < y.l;
}

int main()
{
    int cnt = 0;  // 记录测试用例的编号(Case 1、Case 2...)
    while(cin >> n >> d, n && d)
    {
        cnt++;
        //标记是否存在无法覆盖的岛屿(y > d)
        bool flag = false;
        
        // 遍历所有岛屿,计算每个岛屿对应的雷达覆盖区间
        for(int i = 1; i <= n; i++)
        {
            double x, y; cin >> x >> y;
            //岛屿的y坐标超过雷达半径,垂直距离已超范围,无法覆盖
            if(y > d) flag = true;
            // 勾股定理
            double l = sqrt(d * d - y * y);
            //计算该岛屿对应的雷达覆盖区间:[x-l, x+l]
            a[i].l = x - l;
            a[i].r = x + l;
        }

        cout << "Case " << cnt << ": ";
        // 如果存在无法覆盖的岛屿,直接输出-1
        if(flag) cout << -1 << endl;
        else{
            sort(a + 1, a + 1 + n, cmp);
            //最少雷达数,初始为1(至少需要1个雷达覆盖第一个区间)
            int ret = 1;  
            //用第一个区间右端点作为基准,判断第二个区间是否与其重叠
            double r = a[1].r;
            
            for(int i = 2; i <= n; i++)
            {
                double left = a[i].l;
                double right = a[i].r;
                
                //情况1:当前区间与已覆盖区域重叠(能被当前雷达覆盖)
                if(left <= r)
                {
                    //只有取更小的右端点,才能同时覆盖重叠的所有区间
                    r = min(r, right);
                }
                //请况2:当前区间与已覆盖区域不重叠,需要新增雷达
                else{
                    ret++;
                    //新雷达的最优位置为当前区间的右端点
                    r = right;      
                }
            }
            cout << ret << endl;
        }
    }
    return 0;
}

要点补充:
为什么把所有区间按左端点 l 从小到大排序。

这样我们可以从左到右处理区间,保证每次处理的都是当前最靠左的区间,不会漏掉任何需要覆盖的区域。

while (cin >> n >> d, n && d)如果括号内有一个逗号隔开的话,就是一个逗号表达式,其会从左向右执行,前面cin >> n >> d执行完后,当n和d不为0的时候才会继续执行下一步,二者为0时while循环跳出

1.1.3 Sunscreen

1.1.4 牛栏预定


结语

相关推荐
tankeven2 小时前
HJ129 小红的双生数
c++·算法
万能的小裴同学2 小时前
C++ 简易的FBX查看工具
开发语言·c++·算法
Boop_wu2 小时前
[Java 算法] 前缀和(2)
算法·哈希算法·散列表
SY师弟2 小时前
蓝桥杯单片机——安装芯片包
单片机·嵌入式硬件·职场和发展·蓝桥杯
小宋同学在不断学习2 小时前
蓝桥杯电子赛 __ 定时器1中断实现LED定时翻转
单片机·蓝桥杯·定时器
Hello.Reader2 小时前
深入浅出 Adam 优化算法从直觉到公式
深度学习·算法
识君啊2 小时前
拆分与合并的艺术·分治思想:Java归并排序深度解析
java·数据结构·算法·排序算法·归并排序·分治
Trouvaille ~2 小时前
【贪心算法】专题(六):降维打击与错位重构的终极收官
c++·算法·leetcode·面试·贪心算法·重构·蓝桥杯
2301_800895102 小时前
dijkstra求最短路径--备考蓝桥杯版
算法