OpenCV如何使用 GDAL 读取地理空间栅格文件(72)

返回:OpenCV系列文章目录(持续更新中......)
上一篇:OpenCV的周期性噪声去除滤波器(70)
下一篇 :OpenCV系列文章目录(持续更新中......)

目录

目标

代码:

解释: 

如何使用 GDAL 读取栅格数据

注意

通常应避免使用经度/纬度(地理)坐标

查找拐角坐标

结果

地理空间栅格数据是地理信息系统和摄影测量中大量使用的产品。栅格数据通常可以表示影像和数字高程模型 (DEM)。用于加载 GIS 影像的标准库是地理数据抽象库 (GDAL)。在此示例中,我们将展示使用本机 OpenCV 函数加载 GIS 栅格格式的技术。此外,我们将展示一些示例,说明OpenCV如何将这些数据用于新颖有趣的目的。

目标

本教程的主要目标:

  • 如何使用 OpenCV imread 加载卫星图像。
  • 如何使用 OpenCV imread 加载 SRTM 数字高程模型
  • 给定图像和 DEM 的角坐标,将高程数据与图像相关联,以查找每个像素的高程。
  • 显示一个基本、易于实施的地形热图示例。
  • 显示 DEM 数据与正射校正影像的基本用法。

为了实现这些目标,以下代码采用数字高程模型以及旧金山的 GeoTiff 图像作为输入。对影像和 DEM 数据进行处理并生成影像的地形热图,并标注在海湾水位上升 10、50 和 100 米时将受到影响的城市区域。

代码如下:

/*
 * gdal_image.cpp -- Load GIS data into OpenCV Containers using the Geospatial Data Abstraction Library
*/
 
// OpenCV Headers
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
 
// C++ Standard Libraries
#include <cmath>
#include <iostream>
#include <stdexcept>
#include <vector>
 
using namespace std;
 
// define the corner points
// Note that GDAL library can natively determine this
cv::Point2d tl( -122.441017, 37.815664 );
cv::Point2d tr( -122.370919, 37.815311 );
cv::Point2d bl( -122.441533, 37.747167 );
cv::Point2d br( -122.3715, 37.746814 );
 
// determine dem corners
cv::Point2d dem_bl( -122.0, 38);
cv::Point2d dem_tr( -123.0, 37);
 
// range of the heat map colors
std::vector<std::pair<cv::Vec3b,double> > color_range;
 
 
// List of all function prototypes
cv::Point2d lerp( const cv::Point2d&, const cv::Point2d&, const double& );
 
cv::Vec3b get_dem_color( const double& );
 
cv::Point2d world2dem( const cv::Point2d&, const cv::Size&);
 
cv::Point2d pixel2world( const int&, const int&, const cv::Size& );
 
void add_color( cv::Vec3b& pix, const uchar& b, const uchar& g, const uchar& r );
 
 
 
/*
 * Linear Interpolation
 * p1 - Point 1
 * p2 - Point 2
 * t - Ratio from Point 1 to Point 2
*/
cv::Point2d lerp( cv::Point2d const& p1, cv::Point2d const& p2, const double& t ){
 return cv::Point2d( ((1-t)*p1.x) + (t*p2.x),
 ((1-t)*p1.y) + (t*p2.y));
}
 
/*
 * Interpolate Colors
*/
template <typename DATATYPE, int N>
cv::Vec<DATATYPE,N> lerp( cv::Vec<DATATYPE,N> const& minColor,
 cv::Vec<DATATYPE,N> const& maxColor,
 double const& t ){
 
 cv::Vec<DATATYPE,N> output;
 for( int i=0; i<N; i++ ){
 output[i] = (uchar)(((1-t)*minColor[i]) + (t * maxColor[i]));
 }
 return output;
}
 
/*
 * Compute the dem color
*/
cv::Vec3b get_dem_color( const double& elevation ){
 
 // if the elevation is below the minimum, return the minimum
 if( elevation < color_range[0].second ){
 return color_range[0].first;
 }
 // if the elevation is above the maximum, return the maximum
 if( elevation > color_range.back().second ){
 return color_range.back().first;
 }
 
 // otherwise, find the proper starting index
 int idx=0;
 double t = 0;
 for( int x=0; x<(int)(color_range.size()-1); x++ ){
 
 // if the current elevation is below the next item, then use the current
 // two colors as our range
 if( elevation < color_range[x+1].second ){
 idx=x;
 t = (color_range[x+1].second - elevation)/
 (color_range[x+1].second - color_range[x].second);
 
 break;
 }
 }
 
 // interpolate the color
 return lerp( color_range[idx].first, color_range[idx+1].first, t);
}
 
/*
 * Given a pixel coordinate and the size of the input image, compute the pixel location
 * on the DEM image.
*/
cv::Point2d world2dem( cv::Point2d const& coordinate, const cv::Size& dem_size ){
 
 
 // relate this to the dem points
 // ASSUMING THAT DEM DATA IS ORTHORECTIFIED
 double demRatioX = ((dem_tr.x - coordinate.x)/(dem_tr.x - dem_bl.x));
 double demRatioY = 1-((dem_tr.y - coordinate.y)/(dem_tr.y - dem_bl.y));
 
 cv::Point2d output;
 output.x = demRatioX * dem_size.width;
 output.y = demRatioY * dem_size.height;
 
 return output;
}
 
/*
 * Convert a pixel coordinate to world coordinates
*/
cv::Point2d pixel2world( const int& x, const int& y, const cv::Size& size ){
 
 // compute the ratio of the pixel location to its dimension
 double rx = (double)x / size.width;
 double ry = (double)y / size.height;
 
 // compute LERP of each coordinate
 cv::Point2d rightSide = lerp(tr, br, ry);
 cv::Point2d leftSide = lerp(tl, bl, ry);
 
 // compute the actual Lat/Lon coordinate of the interpolated coordinate
 return lerp( leftSide, rightSide, rx );
}
 
/*
 * Add color to a specific pixel color value
*/
void add_color( cv::Vec3b& pix, const uchar& b, const uchar& g, const uchar& r ){
 
 if( pix[0] + b < 255 && pix[0] + b >= 0 ){ pix[0] += b; }
 if( pix[1] + g < 255 && pix[1] + g >= 0 ){ pix[1] += g; }
 if( pix[2] + r < 255 && pix[2] + r >= 0 ){ pix[2] += r; }
}
 
 
/*
 * Main Function
*/
int main( int argc, char* argv[] ){
 
 /*
 * Check input arguments
 */
 if( argc < 3 ){
 cout << "usage: " << argv[0] << " <image_name> <dem_model_name>" << endl;
 return -1;
 }
 
 // load the image (note that we don't have the projection information. You will
 // need to load that yourself or use the full GDAL driver. The values are pre-defined
 // at the top of this file
 cv::Mat image = cv::imread(argv[1], cv::IMREAD_LOAD_GDAL | cv::IMREAD_COLOR );
 
 // load the dem model
 cv::Mat dem = cv::imread(argv[2], cv::IMREAD_LOAD_GDAL | cv::IMREAD_ANYDEPTH );
 
 // create our output products
 cv::Mat output_dem( image.size(), CV_8UC3 );
 cv::Mat output_dem_flood( image.size(), CV_8UC3 );
 
 // for sanity sake, make sure GDAL Loads it as a signed short
 if( dem.type() != CV_16SC1 ){ throw std::runtime_error("DEM image type must be CV_16SC1"); }
 
 // define the color range to create our output DEM heat map
 // Pair format ( Color, elevation ); Push from low to high
 // Note: This would be perfect for a configuration file, but is here for a working demo.
 color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 188, 154, 46), -1));
 color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 110, 220, 110), 0.25));
 color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 150, 250, 230), 20));
 color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 160, 220, 200), 75));
 color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 220, 190, 170), 100));
 color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 250, 180, 140), 200));
 
 // define a minimum elevation
 double minElevation = -10;
 
 // iterate over each pixel in the image, computing the dem point
 for( int y=0; y<image.rows; y++ ){
 for( int x=0; x<image.cols; x++ ){
 
 // convert the pixel coordinate to lat/lon coordinates
 cv::Point2d coordinate = pixel2world( x, y, image.size() );
 
 // compute the dem image pixel coordinate from lat/lon
 cv::Point2d dem_coordinate = world2dem( coordinate, dem.size() );
 
 // extract the elevation
 double dz;
 if( dem_coordinate.x >= 0 && dem_coordinate.y >= 0 &&
 dem_coordinate.x < dem.cols && dem_coordinate.y < dem.rows ){
 dz = dem.at<short>(dem_coordinate);
 }else{
 dz = minElevation;
 }
 
 // write the pixel value to the file
 output_dem_flood.at<cv::Vec3b>(y,x) = image.at<cv::Vec3b>(y,x);
 
 // compute the color for the heat map output
 cv::Vec3b actualColor = get_dem_color(dz);
 output_dem.at<cv::Vec3b>(y,x) = actualColor;
 
 // show effect of a 10 meter increase in ocean levels
 if( dz < 10 ){
 add_color( output_dem_flood.at<cv::Vec3b>(y,x), 90, 0, 0 );
 }
 // show effect of a 50 meter increase in ocean levels
 else if( dz < 50 ){
 add_color( output_dem_flood.at<cv::Vec3b>(y,x), 0, 90, 0 );
 }
 // show effect of a 100 meter increase in ocean levels
 else if( dz < 100 ){
 add_color( output_dem_flood.at<cv::Vec3b>(y,x), 0, 0, 90 );
 }
 
 }}
 
 // print our heat map
 cv::imwrite( "heat-map.jpg" , output_dem );
 
 // print the flooding effect image
 cv::imwrite( "flooded.jpg", output_dem_flood);
 
 return 0;
}

解释: 

在提供的代码片段中,有几个关键函数,它们负责执行特定的任务,如插值、颜色映射和坐标转换。下面是每个关键函数的代码片段和详细解释:

1. **线性插值函数 `lerp`**

cv::Point2d lerp(cv::Point2d const& p1, cv::Point2d const& p2, const double& t){
    return cv::Point2d(((1 - t) * p1.x) + (t * p2.x), 
                       ((1 - t) * p1.y) + (t * p2.y));
}


这个函数执行二维空间中的线性插值。它接受两个点 `p1` 和 `p2`,以及一个插值比率 `t`。当 `t` 从 0 变到 1 时,这个函数会生成从 `p1` 到 `p2` 的一系列点。

2. **颜色插值模板函数 `lerp`**

template <typename DATATYPE, int N>
cv::Vec<DATATYPE,N> lerp(cv::Vec<DATATYPE,N> const& minColor,
                           cv::Vec<DATATYPE,N> const& maxColor,
                           double const& t){
    cv::Vec<DATATYPE,N> output;
    for(int i = 0; i < N; i++){
        output[i] = static_cast<uchar>(((1 - t) * minColor[i]) + (t * maxColor[i]));
    }
    return output;
}


这是一个模板函数,用于在两种颜色之间进行插值。它接受两种颜色 `minColor` 和 `maxColor`,以及插值比率 `t`。对于颜色中的每个通道(对于 `cv::Vec3b` 是三个通道),它计算插值后的颜色值。

3. **计算 DEM 颜色 `get_dem_color`**

cv::Vec3b get_dem_color(const double& elevation){
    // ... 省略了检查颜色范围和插值的代码 ...
    return lerp(color_range[idx].first, color_range[idx+1].first, t);
}


这个函数根据给定的海拔高度 `elevation` 从预定义的颜色范围内获取对应的颜色。它首先检查海拔高度是否在颜色范围的边界之外,然后找到合适的颜色对进行插值计算。

4. **坐标转换函数 `world2dem`**

cv::Point2d world2dem(const cv::Point2d& coordinate, const cv::Size& dem_size){
    // ... 省略了计算比例和输出点坐标的代码 ...
    return output;
}


此函数将世界坐标转换为 DEM 图像的像素坐标。它接受一个世界坐标 `coordinate` 和 DEM 图像的尺寸 `dem_size`,然后计算对应的像素坐标。

5. **坐标转换函数 `pixel2world`**

cv::Point2d pixel2world(const int& x, const int& y, const cv::Size& size){
    // ... 省略了计算比例和插值坐标的代码 ...
    return lerp(leftSide, rightSide, rx);
}


这个函数将像素坐标转换为世界坐标。它接受一个像素坐标 `(x, y)` 和图像的尺寸 `size`,然后计算出对应的世界坐标。

6. **添加颜色到像素 `add_color`**

void add_color(cv::Vec3b& pix, const uchar& b, const uchar& g, const uchar& r){
    // ... 省略了颜色添加的代码 ...
}


此函数将特定的颜色值(蓝、绿、红通道)添加到像素中。它确保添加的颜色值不会超过 255(因为颜色值是以 0 到 255 的整数表示的)。

7. **主函数 `main`**

int main(int argc, char* argv[]){
    // ... 省略了加载图像、DEM 数据和颜色范围设置的代码 ...
    for(int y = 0; y < image.rows; y++){
        for(int x = 0; x < image.cols; x++){
            // ... 省略了像素处理的代码 ...
        }
    }
    // ... 省略了保存图像的代码 ...
    return 0;
}


`main` 函数是程序的入口点。它首先检查命令行参数,然后加载所需的图像和 DEM 数据。接着,它通过两个嵌套循环遍历图像的每个像素,使用上述函数来计算世界坐标、DEM 坐标、海拔高度和颜色,并根据这些信息生成热图和洪水效果图像。

这些函数共同工作,实现了一个地理空间数据可视化的程序,它可以根据 DEM 数据生成热图,并且模拟不同海拔高度下的洪水效果。

如何使用 GDAL 读取栅格数据

此演示使用默认的 OpenCV imread 函数。主要区别在于,为了强制 GDAL 加载映像,您必须使用适当的标志。

 cv::Mat image = cv::imread(argv[1], cv::IMREAD_LOAD_GDAL | cv::IMREAD_COLOR );

加载数字高程模型时,每个像素的实际数值是必不可少的,不能缩放或截断。例如,对于图像数据,表示为值为 1 的双精度值的像素与表示为值为 255 的无符号字符的像素具有相同的外观。对于地形数据,像素值表示以米为单位的高程。为了确保 OpenCV 保留本机值,请在 imread 中使用 GDAL 标志和 ANYDEPTH 标志。

 // load the dem model
 cv::Mat dem = cv::imread(argv[2], cv::IMREAD_LOAD_GDAL | cv::IMREAD_ANYDEPTH );

如果您事先知道要加载的 DEM 模型的类型,那么使用断言或其他机制测试 Mat::type() 或 Mat::d epth() 可能是一个安全的选择。NASA 或 DOD 规范文档可以提供各种高程模型的输入类型。主要类型,SRTM 和 DTED,都是签名短裤。

注意

通常应避免使用经度/纬度(地理)坐标

地理坐标系是一个球面坐标系,这意味着将它们与笛卡尔数学一起使用在技术上是不正确的。此演示使用它们来增加可读性,并且足够准确以说明重点。更好的坐标系是通用横轴墨卡托坐标系。

查找拐角坐标

查找图像角坐标的一种简单方法是使用命令行工具 gdalinfo。对于正射校正且包含投影信息的影像,可以使用 USGS EarthExplorer。

\f$> gdalinfo N37W123.hgt
 
 Driver: SRTMHGT/SRTMHGT File Format
 Files: N37W123.hgt
 Size is 3601, 3601
 Coordinate System is:
 GEOGCS["WGS 84",
 DATUM["WGS_1984",
 
 ... more output ...
 
 Corner Coordinates:
 Upper Left (-123.0001389, 38.0001389) (123d 0' 0.50"W, 38d 0' 0.50"N)
 Lower Left (-123.0001389, 36.9998611) (123d 0' 0.50"W, 36d59'59.50"N)
 Upper Right (-121.9998611, 38.0001389) (121d59'59.50"W, 38d 0' 0.50"N)
 Lower Right (-121.9998611, 36.9998611) (121d59'59.50"W, 36d59'59.50"N)
 Center (-122.5000000, 37.5000000) (122d30' 0.00"W, 37d30' 0.00"N)
 
 ... more output ...

结果

以下是程序的输出。使用第一个图像作为输入。对于 DEM 模型,请在此处下载位于 USGS 的 SRTM 文件。

http://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_04/N37W123.hgt.zip

输入图像

热图

热图叠加


参考文献:

1、《Reading Geospatial Raster files with GDAL》-----Marvin Smith

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/593459.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

05_G1垃圾收集器

G1垃圾收集器简介 垃圾优先 Garbage-First&#xff08;G1&#xff09;垃圾收集器面向多处理器机器&#xff0c;适用于大内存场景。它尝试在无需太多配置的情况下实现垃圾收集暂停时间目标&#xff0c;并同时实现高吞吐量。G1旨在通过适用于当前目标应用和环境的功能&#xff0…

go mod

常用命令 初始化模块 go mod init 模块名下载 go.mod 文件中指明的所有依赖 go mod download github.com/gin-gonic/ginv1.9.(依赖路径)依赖对其&#xff08;使引用的都是所依赖的&#xff09; go mod tidy编辑go.mod go mod edit go mod edit -require"github.com/g…

记录几种排序算法

十种常见排序算法可以分类两大类别&#xff1a;比较类排序和非比较类排序。 常见的快速排序、归并排序、堆排序以及冒泡排序等都属于比较类排序算法。比较类排序是通过比较来决定元素间的相对次序&#xff0c;其时间复杂度不能突破 O(nlogn)。在冒泡排序之类的排序中&…

数据结构---时间复杂度+空间复杂度

算法(algorithm)简单说就是解决问题的方法。方法有好坏&#xff0c;同样算法也是&#xff0c;有效率高的算法&#xff0c;也有效率低的算法。衡量算法的好坏一般从时间和空间两个维度衡量&#xff0c;也就是本文要介绍的时间复杂度和空间复杂度。有些时候&#xff0c;时间与空间…

js api part3

环境对象 环境对象&#xff1a; 指的是函数内部特殊的 变量 this &#xff0c; 它代表着当前函数运行时所处的环境 作用&#xff1a; 弄清楚this的指向&#xff0c;可以让我们代码更简洁 函数的调用方式不同&#xff0c;this 指代的对象也不同 【谁调用&#xff0c; this 就是…

springboot模块以及非springboot模块构成的多模块maven项目最佳构建方式

文章目录 背景一般的实现使用spring-boot-dependencies 更优雅的实现. 背景 有时候构建一个多模块maven项目其中某一个模块是web-service需要使用spring boot,其他模块跟spring boot 完全无关,本文总结一下在这个场景下maven项目最佳构建方式. 一般的实现 网上应该也看到过很…

智能工业相机哪家好?

一、什么是智能工业相机 在工业自动化的浪潮中&#xff0c;智能工业相机扮演着至关重要的角色。它们如同工业领域的“眼睛”&#xff0c;为生产过程提供精准的视觉监测和数据采集。然而&#xff0c;面对众多的智能工业相机品牌&#xff0c;如何选择一款真正适合的产品成为了众多…

企业开发基础--数据库

今天完成了数据库学习的全部内容&#xff0c;在事务&#xff0c;索引&#xff0c;范式中要有个人逻辑上的理解&#xff0c;也算是卡着点完成了大多数预期&#xff0c;还有一个Java游戏未完成&#xff0c;会后续补上。 之后的一周要完成34道数据库练习题以及JDBC&#xff0c;学…

88、动态规划-乘积最大子数组

思路&#xff1a; 首先使用递归来解&#xff0c;从0开始到N&#xff0c;每次都从index开始到N的求出最大值。然后再次递归index1到N的最大值&#xff0c;再求max。代码如下&#xff1a; // 方法一&#xff1a;使用递归方式找出最大乘积public static int maxProduct(int[] num…

Graph RAG:基于知识图谱的检索增强技术与优势对比

身处信息爆炸时代&#xff0c;如何从海量信息中获取准确全面的搜索结果&#xff0c;并以更直观、可读的方式呈现出来是大家期待达成的目标。传统的搜索增强技术受限于训练文本数量、质量等问题&#xff0c;对于复杂或多义词查询效果不佳&#xff0c;更无法满足 ChatGPT 等大语言…

【Linux】进程间通信 - 管道

文章目录 1. 进程间通信介绍1.1 进程间通信目的1.2 进程间通信发展1.3 进程间通信分类 2. 管道2.1 什么是管道2.2 匿名管道2.3 用 fork 来共享管道原理2.4 站在文件描述符角度 - 深入理解管道2.5 站在内核角度 - 管道本质2.6 管道读写规则2.7 管道特点 3. 命名管道3.1 匿名管道…

C语言实战项目--贪吃蛇

贪吃蛇是久负盛名的游戏之一&#xff0c;它也和俄罗斯⽅块&#xff0c;扫雷等游戏位列经典游戏的行列。在编程语言的教学中&#xff0c;我们以贪吃蛇为例&#xff0c;从设计到代码实现来提升大家的编程能⼒和逻辑能⼒。 在本篇讲解中&#xff0c;我们会看到很多陌生的知识&…

牛角源码 | 【独立版】商城盲盒源码带uniapp(H5+小程序+APP三端)全开源

前端uniapp开源代码&#xff0c;可用HBuilder工具无限发行H5、小程序和打包app&#xff0c;后端PHP开源源码&#xff0c;支持二开。 内有安装搭建教程&#xff0c;轻松部署&#xff0c;搭建即可运营&#xff0c;内置永久免费更新地址&#xff0c;后续无忧升级。 下载地址&…

window 安装ai 基础环境(yolo8,训练推理等)

安装步骤: 1. python sdk 3.9以上&#xff1a;选择 3.9.13, 不知道为什么 3.9.0-0a等安装pytorch 不行。 2. 显卡驱动 可以使用驱动精灵 直接安装N 卡推荐 3. 安装机器学习套件CUDA cuda 安装在PyTorch 需要根 PyTorch版本一致&#xff0c;我的 win-srv 最高支持 12.1 …

专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(五)

本系列课程&#xff0c;将重点讲解Phpsploit-Framework框架软件的基础使用&#xff01; 本文章仅提供学习&#xff0c;切勿将其用于不法手段&#xff01; 继续接上一篇文章内容&#xff0c;讲述如何进行Phpsploit-Framework软件的基础使用和二次开发。 在下面的图片中&#…

星辰考古:TiDB v1.0 再回首

“ 1.0 版本只是个开始&#xff0c;是新的起点&#xff0c;愿我们一路相扶&#xff0c;不负远途。 前言 TiDB 是 PingCAP 公司自主设计、研发的开源分布式关系型数据库。 近日&#xff0c;TiDB v8.0.0 DMR 发布&#xff0c;详细发版说明戳这里&#xff1a; https://docs.pingca…

2024年Q1季度防晒霜数据分析:个性化与差异化成为破局关键

五一出游期间&#xff0c;防晒必不可少&#xff0c;尤其是随着“颜值经济”的崛起&#xff0c;防晒霜成为了许多消费者出游时的必备选择。但随着“物理防晒”、“硬防晒”等概念的推出&#xff0c;防晒霜作为“化学防晒”的代表&#xff0c;在今年Q1季度线上市场表现受到影响。…

ICode国际青少年编程竞赛- Python-1级训练场-变量入门

ICode国际青少年编程竞赛- Python-1级训练场-变量入门 1、 a 4 Dev.turnRight() Dev.step(a)2、 a 4 Spaceship.step(a) Dev.step(a)3、 a 4 Dev.step(a) Dev.turnLeft() Dev.step(a)4、 a 5 Dev.step(a) Spaceship.step(a) Dev.step(a)5、 a 3 Dev.step(a) Dev.tur…

轨道交通巡检机器人的应用范围

在现代轨道交通系统的庞大网络中&#xff0c;无数的轨道、设备和设施交织在一起&#xff0c;如同一个精密的机器在高效运转。而在这背后&#xff0c;轨道交通巡检机器人正悄然登场&#xff0c;它们如同一个个智能的守护者&#xff0c;穿梭于各个场景之中。那么&#xff0c;这些…

【LeetCode:1235. 规划兼职工作 + 动态规划 + 二分查找】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…
最新文章