8-3分治

news2024/11/26 0:43:16

引入

分治算法(divide and conquer)是五大常用算法(分治算法、动态规划算法、贪心算法、回溯法、分治界限法)之一。其实,很多人在平时学习中已经不知不觉就用到了分治算法,只是不知道那就是分治算法,今天,我们就来全面地去认识和了解分治算法。

在学习分治算法之前,问你一个问题,相信大家小时候都有存钱罐的经历,父母亲人如果给钱都会往自己的宝藏中存钱,我们每隔一段时间都会清点清点钱。但是一堆钱让你处理起来你可能觉得很复杂,因为数据相对于大脑有点庞大了,并且很容易算错,你可能会将它 先分 成几个小份算,然后 再叠加 起来计算总和就获得这堆钱的总数了。

image-20201130124009617

当然如果你觉得各个部分钱数量还是太大,你依然可以进行划分然后合并,我们之所以这么多是因为:计算每个小堆钱的方式和计算最大堆钱的方式是相同的(区别在于体量上)然后大堆钱总和其实就是小堆钱结果之和。这样其实就有一种分治的思想。当然这些钱都是想出来的……

分治算法的思想

分治算法是用了分治思想的一种算法,什么是分治?

分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。在计算机科学中,分治法就是运用分治思想的一种很重要的算法。分治法是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)等等。

将父问题分解为子问题同等方式求解,这和递归的概念很吻合,所以在分治算法通常以递归的方式实现(当然也有非递归的实现方式)。分治算法的描述从字面上也很容易理解,分、治其实还有个合并的过程:

· 分(Divide):递归解决较小的问题(到终止层或者可以解决的时候停下)

· 治(Conquer):递归求解,如果问题够小直接求解。

· 合并(Combine):将子问题的解构建父类问题

一般分治算法在正文中分解为两个即以上的递归调用,并且子类问题一般是不想交的(互不影响)。当求解一个问题规模很大很难直接求解,但是规模较小的时候问题很容易求解并且这个问题并且问题满足分治算法的适用条件,那么就可以使用分治算法

那么采用分治算法解决的问题需要 满足那些条件(特征) 呢?

1 . 原问题规模通常比较大,不易直接解决,但问题缩小到一定程度就能较容易的解决。

2 . 问题可以分解为若干规模较小、求解方式相同(似)的子问题,且子问题之间求解是独立的互不影响。

3 . 合并问题分解的子问题可以得到问题的解。

你可能会疑惑分治算法和递归有什么关系?其实分治重要的是一种 思想 ,注重的是 问题分合并的过程。而分治中的递归是一种方式(工具),这种方式通过函数自己调用自己,但是通过修改函数的参数来达到将问题分解、使问题规模变小的目的。

分治算法经典问题

对于分治算法的经典问题,重要的是其思想,因为我们大部分借助递归去实现,所以在代码实现上大部分都是很简单。分治算法的经典问题,个人将它分成两大类:子问题完全独立和子问题不完全独立。

1 . 子问题完全独立就是原问题的答案可完全由子问题的结果推出。

2 . 子问题不完全独立,有些 区间类的问题或者跨区间问题 使用分治可能结果跨区间,在考虑问题的时候需要仔细借鉴下。

分治算法解决问题主要有三个步骤:

Divide(切分子问题的方案)

Conquer(一般子问题独立相同的,所以这里一般是递归的解决子问题)

Combine(子问题提升至更大问题的时候需要对子问题的解决方案进行合并)

分治算法对待不同的问题需要不同的分治方案,所以掌握缩小问题的规模的思想是非常重要的,比较高级的动态规划和贪心也都是通过缩小问题规模来提升时间复杂度的。所以通过练习,多掌握一些具体实例的分治方案,这样碰到陌生问题的时候可以像熟悉的问题靠拢,这样才容易解决陌生的问题。

二分查找(搜索)——P2499 二分递归查找(search)

二分搜索是分治的一个实例,只不过二分搜索有着自己的特殊性

序列是有序

的结果为一个值

正常二分将一个完整的区间分成两个区间,两个区间本应单独找值然后确认结果,但是通过有序的区间可以直接确定结果在哪个区间,所以分的两个区间只需要计算其中一个区间,然后继续进行一直到结束。实现方式有递归和非递归,但是非递归(while循环)用的更多一些。

方法一:递归二分

二分查找即可,分左中右三段判断

#include <bits/stdc++.h>
using namespace std;

int a[1000], n, k;

int search(int l, int r)
{
    if(l >= r)         // 搜到头了,返回结果
        return a[l]==k?l:-1;
    int mid = l+r>>1;
    if(a[mid] == k)    // 搜到了
        return mid;
    else if(a[mid]>k)
        return search(l, mid-1);  // 在左半边,搜
    else
        return search(mid+1, r);  // 在右半边,搜
}

int main()
{
    cin >> n >> k;
    for(int i = 1; i <= n; i++)
        cin >> a[i];
    cout << search(1,n);
    return 0;
}

方法二、循环二分

题目要求用递归实现,这是出于学习递归二分的目的,本题也可以用循环二分实现,定义 二分中的三个关键变量 int l, r, mid; 循环查找区域即可。

#include <bits/stdc++.h>
using namespace std;

int a[1000], n, k;

int main()
{
    cin >> n >> k;
    for(int i = 1; i <= n; i++)
        cin >> a[i];
    int l = 1, r = n, mid;   // 初始二分范围
    while(l <= r)
    {
        mid = l+r>>1;        // 二分
        if(a[mid] > k)       // 查找左半边
            r = mid-1;
        else if(a[mid] == k) // 中间,找到就返回
        {
            cout << mid;
            return 0;
        }
        else                 // 右半边
            l = mid+1;
    }
    cout << -1; 
    return 0;
}

方法三、暴力枚举

因为数据范围太小了,本题也可以用暴力枚举去直接循环查找,但是......弱爆了,如果数据范围达到 10^8级别以上,就会超时了

#include<bits/stdc++.h>
using namespace std;
long long n,k,a[1001],b[1001],flag=1;
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        if(a[i]==k)
        {
            cout<<i;
            flag=0;
        }
    }
    if(flag==1)
    {
        cout<<-1;
    }
    return 0;
}


方法四:另辟蹊径

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,k,s=0;
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        int t;
        cin>>t;
        if(t==k)
        {
            s=i;
        }
    }
    cout<<(s==0?-1:s);
}

归并排序

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

分而治之

可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。分阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n。

合并相邻有序子序列 

再来看看阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。

 归并排序(逆序数)——P2612 归并排序

快排在分的时候做了很多工作,而归并就是相反,归并在分的时候按照数量均匀分,而合并时候已经是两两有序的进行合并的,因为两个有序序列O(n)级别的复杂度即可得到需要的结果。而逆序数在归并排序基础上变形同样也是分治思想求解。

归并排序参考代码

方法一:归并实现

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6+5;
int n, a[N], tmp[N];

void merge_sort(int q[], int l, int r)
{
    if (l >= r) return;

    // 分(递归)
    int mid = l + r >> 1;         // 将数组平均分成两个部分
    merge_sort(q, l, mid);        // 对左半部分进行分治排序
    merge_sort(q, mid + 1, r);    // 对右半部分进行分治排序
    
    // 治(合并)
    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
        else tmp[k ++ ] = q[j ++ ];

    while (i <= mid) tmp[k ++ ] = q[i ++ ];   // 可能的左半部分比右半部分多的
    while (j <= r) tmp[k ++ ] = q[j ++ ];     // 可能的右半部分比左半部分多的

    for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}

int main()
{
    cin >> n;
    for(int i = 0; i < n; i++)
        cin >> a[i];
    
    merge_sort(a, 0, n-1);
    
    for(int i = 0; i < n; i++)
        cout << a[i] << ' ';
    return 0;
}

方法二:另辟蹊径 

#include<bits/stdc++.h>
using namespace std;
long long n,a[1000005];
int main()
{
   cin>>n;
   for(int i=1;i<=n;i++)    cin>>a[i];
   sort(a+1,a+n+1);
   for(int i=1;i<=n;i++)    cout<<a[i]<<" ";
}

未完————下期继续 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Word Embedding

One-hot-encoding 缺点 1.向量维度和向量个数很大&#xff0c;假设有1w个token的话&#xff0c;向量个数和维度就都是1w 2. 语义相近的词的向量并不相似 Word Embedding 核心思想&#xff1a;可以通过上下文理解单词的语义 predection-based方法 使用前一个单词预测下一个…

【前端】1、flex 布局详解

flex 布局目录 一、flex container 和 flex items二、用在 flex container 上的 CSS 属性(1) flex-direction(2) justify-content(3) align-items(4) flex-wrap(5) flex-flow(6) align-content 三、用在 flex items 上的 CSS 属性(1) order&#xff08;顺序&#xff09;(2) ali…

【Linux】Linux下的基本指令

&#x1f61b;作者&#xff1a;日出等日落 &#x1f4d8; 专栏&#xff1a;数据结构 人生就是这样&#xff0c;要耐的住寂寞&#xff0c;才守得住繁华。 —— 七堇年 目录 Linux的基本命令(常用)&#xff1a; ls 指令&#xff1a; pwd指…

告别混乱代码,这份 SpringBoot 后端接口规范太及时了

告别混乱代码&#xff0c;这份 SpringBoot 后端接口规范太及时了&#xff01; 文章目录 一、前言 二、环境说明 三、参数校验 1、介绍2、Validator 自动抛出异常&#xff08;使用&#xff09;3、分组校验和递归校验4、自定义校验 四、全局异常处理 1、基本使用2、自定义…

【Android入门到项目实战-- 9.4】—— 方向传感器的详细使用教程

目录 一、基础知识 二、实战使用 一、基础知识 Android的方向传感器&#xff0c;返回三轴的角度数据&#xff0c;方向数据的单位是角度。 提供三个数据&#xff1a;azimuth、pitch和roll。 azimuth&#xff1a;方位&#xff0c;返回水平时磁北极和Y轴的夹角&#xff0c;范围是…

Qt绘图(好玩)

release下的exe文件流畅度要远好于debug下的exe文件。 源码来源&#xff1a;基于Qt5模拟企业微信聊天界面(QWidget)_阿木大叔mu的博客-CSDN博客 初看时&#xff0c;觉得很神奇&#xff0c;猫眼会随着鼠标移动。 看完源码后&#xff0c;感觉很精美。 全是用painter画上去的&…

09_Uboot启动流程_1

目录 链接脚本u-boot.lds详解 U-Boot启动流程详解 reset函数源码详解 lowlevel_init函数详解 s_init函数详解 链接脚本u-boot.lds详解 要分析uboot的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入…

【射影几何08】仿射映射

一、说明 简单来说&#xff0c;“仿射变换”就是&#xff1a;“线性变换”“平移”&#xff0c;但这是在笛卡尔坐标下的表现&#xff0c;然而在射影几何中&#xff0c;其中有更合乎逻辑的解释。本文讲仿射映射的定义&#xff0c;以及仿射不变性的特点。 二、仿射映射 2.1 直线…

2022 年全国硕士研究生入学统一考试英语(二)试题

2022年全国硕士研究生入学统一考试英语&#xff08;二&#xff09;试题 SectionⅠUse of English Directions: Read the following text. Choose the best word(s) for each numbered blank and mark A, B, C or D on the ANSWER SHEET. (10 points) Harlan Coben believes tha…

ChatGPT诞生的新岗位:提示工程师(Prompt Engineer)

ChatGPT诞生的新岗位&#xff1a;提示工程师&#xff08;Prompt Engineer&#xff09; Prompt 工程师是什么? 是识别人工智能的错误和隐藏功能&#xff0c;以便开发者可以对这些发现进行处理。 如果你正在寻找科技领域最热门的工作&#xff0c;你可以尝试了解如何与AI聊天机…

分布式文件存储系统Minio使用总结

分布式文件存储系统Minio使用总结 1.分布式文件系统应用: 1.1、Minlo 介绍&#xff1a; Minlo 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口&#xff0c;非常适合于存储大容量非结构化的数据&#xff0c;例如图片、视频、日志文件、…

一篇文章带你了解抖音来客功能的使用方法和注意事项

抖音是近年来备受欢迎的社交媒体平台之一&#xff0c;其中的“来客”功能更是让许多人喜爱。那么什么是抖音来客呢&#xff1f;抖音来客是指在直播过程中&#xff0c;可以邀请其他抖音用户进行互动和参与&#xff0c;从而增加直播的热度和粉丝数量。下面不若与众科技就来介绍一…

FPGA - 7系列 FPGA内部结构之CLB -03- CLB相关原语以及应用

前言 本文节选UG474的第二章&#xff0c;进行整理翻译。CLB资源被FPGA综合工具自动有效地使用&#xff0c;不需要任何特殊的FPGA专用编码。一些HDL编码建议和技术可以帮助优化设计以获得最大效率。 设计检查清单 这些指南是为有效使用7系列CLB的设计建议提供的快速核对表。7…

阿里云服务器公网带宽计费模式选择方法(一篇搞定)

阿里云服务器公网带宽计费模式按固定带宽和按使用流量哪个划算&#xff1f;阿里云百科以北京地域为例&#xff0c;按固定带宽计费1M带宽一个月23元&#xff0c;按使用流量计费1GB流量0.8元&#xff0c;如果云服务器带宽使用率低于10%&#xff0c;那么首选按使用流量计费&#x…

springboot+vue心灵治愈交流平台(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的心灵治愈交流平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风…

【DAY48】移动端布局

const autoprefixer require(‘autoprefixer’) const pxtoviewport require(‘postcss-px-to-viewport’)module.exports { css: { loaderOptions: { postcss: { postcssOptions: { plugins: [ autoprefixer(), pxtoviewport({ viewportWidth: 375, // 视窗宽度&#xff0c…

MySql命令报错:Duplicate entry ‘10‘ for key ‘PRIMARY‘解决方案

MySql命令报错&#xff1a;Duplicate entry ‘10‘ for key ‘PRIMARY‘解决方案 翻译错误先正常制作表格错一遍&#xff08;体验报错&#xff09;找到报错原因&#xff0c;并改正其他可能造成报错的情况总结解决方案 翻译错误 经常遇到这个问题&#xff0c;今天我把这个问题记…

基于session实现共享登录

基于session实现登录 1.发送短信验证码 Override public Result sendCode(String phone, HttpSession session) {//1.校验手机号是否合规if (RegexUtils.isPhoneInvalid(phone)) {//2.不合规直接返回 错误信息return Result.fail("手机号错误");}//3.如果合规生成验…

Ubuntu 安装Samba

每次装ubuntu虚拟机搞开发都要装Samba&#xff0c;记录一下Samba安装方法&#xff1a; 1.输入安装指令 需要使用root权限进行samba的安装操作&#xff1b; 输入指令 &#xff1a; apt-get update apt-get install samba samba-common 2.配置Samba 输入指令修改 Samba配置 &…

GEE:变异系数法在遥感影像分析中的应用及权重计算

作者:CSDN @ _养乐多_ 本文介绍了在Google Earth Engine(GEE)平台上基于变异系数法的多指标加权遥感影像分析方法。该方法通过计算每个指标的平均值和标准差,进而计算变异系数来评估指标的变化程度。利用变异系数,我们可以计算每个指标的权重,并将其应用于加权和指数的计…