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

目录

  • 前言
  • 一、贪心算法
    • [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 牛栏预定


结语

相关推荐
m0_6294947313 小时前
LeetCode 热题 100-----26.环形链表 II
数据结构·算法·leetcode·链表
壹号用户13 小时前
用队列实现栈
数据结构·算法
做人求其滴14 小时前
面试经典 150 题 380 274
c++·算法·面试·职场和发展·力扣
daad77714 小时前
记一组无人机IMU传感器数据
算法
计算机安禾14 小时前
【c++面向对象编程】第42篇:模板特化与偏特化:为特定类型定制实现
开发语言·c++·算法
小O的算法实验室14 小时前
2026年KBS,流形感知强化学习差分进化算法+不规则3D无人机路径规划,深度解析+性能实测
算法·智能算法·智能算法改进
玖釉-14 小时前
C++ 中的循环语句详解:while、do...while、for、嵌套循环与循环控制
开发语言·c++·算法
不做无法实现的梦~14 小时前
运动控制系统复习一览-----常考题目总结版本
算法
小短腿的代码世界15 小时前
信号路由风暴:Qt算法交易系统的高频信号分发架构
qt·算法·架构
阿文的代码库15 小时前
一文读懂GROUP BY 1,2 VS GROUP BY column_1, column_2 的区别
算法