【算法】分治

分治

  • 1.逆序对
  • 2.求第 k 小的数
  • 3.最大子段和
  • 4.地毯填补问题

分治,字面上的解释是「分而治之」,就是把一个复杂的问题分成两个或更多的相同的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。

1.逆序对

P1908 逆序对

在这里插入图片描述

解法:分治

「分治」是解决「逆序对」⾮常经典的解法,也可以利用「树状数组」或「线段树」解决逆序对问题。

如果把整个序列 [l, r] 从中间 mid 位置分成两部分,那么逆序对个数可以分成三部分:

  1. [l, mid] 区间内逆序对的个数 c1。
  2. [mid + 1, r] 区间内逆序对的个数 c2。
  3. 从 [l, mid] 以及 [mid + 1, r] 各选一个数,能组成的逆序对的个数 c3。

那么逆序对的总数就是 c1 + c2 + c3。其中求解 c1,c2的时候跟原问题是一样的,可以交给「递归」去处理,那我们重点处理「一左一右」的情况。

如果在处理一左一右的情况时,两个数组「无序」,我们的时间复杂度其实并没有优化到哪去,甚至还「不如暴力解法」。但是如果两个数组有序的话,我们就可以快速找出逆序对的个数。

先不管怎么求逆序对,我们能让左右两个数组有序嘛?就是「归并排序」。因此,我们能做到在求完 c1, c2 之后,然后让「左右两个区间有序」。那么接下来问题就变成,已知两个「有序数组」,如何求出左边选一个数,右边选一个数的情况下的逆序对的个数。核心思想就是找到右边选一个数之后,左边区间内「有多少个比我大的」。

在这里插入图片描述

定义两个「指针」扫描两个有序数组,此时会有下面三种情况:

  1. a[cur1] ≤ a[cur2]:a[cur1] 不会与 [cur2, r] 区间内任何一个元素构成逆序对,cur1++。
  2. a[cur1] > a[cur2]:此时 [cur1, mid] 区间内所有元素都会与 a[cur2] 构成逆序对,逆序对个数增加,mid − cur1 + 1,此时 cur2 已经统计过逆序对了,cur2++。

重复上面两步,我们就可以在 O(N) 的时间内处理完「一左一右」时,逆序对的个数。而且,我们会发现,这跟我们「归并排序的过程」是高度一致的。所以可以一边排序,一边计算逆序对的个数。

#include<iostream>
using namespace std;typedef long long LL;const int N = 5e5 + 10;int n;
int a[N];
int t[N]; //辅助数组LL merge(int left, int right)
{if(left == right) return 0;LL ret = 0;int mid = (left + right) / 2;ret += merge(left, mid);ret += merge(mid + 1, right);//一左一右的情况int cur1 = left, cur2 = mid + 1, i = left;while(cur1 <= mid && cur2 <= right){if(a[cur1] <= a[cur2]) t[i++] = a[cur1++];else{ret += mid - cur1 + 1;t[i++] = a[cur2++];}}while(cur1 <= mid) t[i++] = a[cur1++];while(cur2 <= right) t[i++] = a[cur2++];for(int j = left; j <= right; j++) a[j] = t[j];return ret;
}int main()
{cin >> n;for(int i = 1; i <= n; i++) cin >> a[i];cout << merge(1, n) << endl;return 0;
}

2.求第 k 小的数

P1923 【深基9.例4】求第 k 小的数

在这里插入图片描述

解法:分治

在快排中,当我们把数组「分成三块」之后: [l, left] [left + 1, right - 1] [right, r] ,我们可以通过计算每一个区间内元素的「个数」,进而推断出我们要找的元素是在「哪一个区间」里面。

那么我们可以直接去「相应的区间」去寻找最终结果就好了。

#include<iostream>
#include<ctime>
using namespace std;const int N = 5e6 + 10;int n, k;
int a[N];int GetRandom(int left, int right)
{return a[rand() % (right - left + 1) + left];
}int QuickSelect(int left, int right, int k)
{if(left == right) return a[left];//1.随机选择基准值int key = GetRandom(left, right);//2.数组分三块int l = left - 1, r = right + 1, cur = left;while(cur < r){if(a[cur] < key) swap(a[++l], a[cur++]);else if(a[cur] == key) cur++;else swap(a[cur], a[--r]);}//3.选择存在最终结果的区间//[left, l] [l + 1, r - 1] [r, right]int a = l - left + 1, b = r - 1 - l, c = right - r + 1;if(k <= a) return QuickSelect(left, l, k);else if(k <= a + b) return key;else return QuickSelect(r, right, k - a - b);
}int main()
{srand(time(0));scanf("%d%d", &n, &k);k++;for (int i = 1; i <= n; i++) scanf("%d", &a[i]); //cin、cout超时cout << QuickSelect(1, n, k) << endl;return 0;
}

3.最大子段和

P1115 最大子段和

在这里插入图片描述

解法:分治

如果把整个序列 [l, r] 从中间 mid 位置分成两部分,那么整个序列中「所有的子数组」就分成三部分:

  1. 子数组在区间 [l, mid] 内。
  2. 子数组在区间 [mid + 1, r] 内。
  3. 子数组的左端点在 [l, mid] 内,右端点在 [mid + 1, r] 内。

在这里插入图片描述

那么我们的「最终结果」也会在这三部分取到,要么在左边区间,要么在右边区间,要么在跨越中轴线的区间。因此,我们可以先求出左边区间的最大子段和,再求出右边区间的最大子段和,最后求出中间区间的最大子段和。其中求「左右区间」时,可以交给「递归」去解决。

那我们重点处理如何求出「中间区间」的最大子段和。可以把中间区间分成两部分:

  1. 左边部分是从 mid 为起点,「向左延伸」的最大子段和。
  2. 右边部分是从 mid + 1 为起点,「向右延伸」的最大子段和。

分别求出这两个值,然后相加即可。求法也很简单,直接「固定起点」,一直把「以它为起点的所有子数组」的和都计算出来,取最大值即可。

#include<iostream>
using namespace std;const int N = 2e5 + 10;int n;
int a[N];int dfs(int left, int right)
{if(left == right) return a[left];int mid = (left + right) / 2;//先求出左右两边的最大值int ret = max(dfs(left, mid), dfs(mid + 1, right));//求出以a[mid]开始,向左延伸的最大值int sum = a[mid], lmax = a[mid];for(int i = mid - 1; i >= left; i--){sum += a[i];lmax = max(lmax, sum);}//求出以a[mid+1]开始,向右延伸的最大值sum = a[mid + 1]; int rmax = a[mid + 1];for(int i = mid + 2; i <= right; i++){sum += a[i];rmax = max(rmax, sum);}//求三者的最大值ret = max(ret, lmax + rmax);return ret;
}int main()
{cin >> n;for(int i = 1; i <= n; i++) cin >> a[i];cout << dfs(1, n) << endl;return 0;
}

4.地毯填补问题

P1228 地毯填补问题

在这里插入图片描述

解法:分治

非常经典的一道分治题目,也可以说是一道递归题目。

一维分治的时候,我们是从中间把整个区间切开,分成左右两部分(其实有时候我们可以三等分,就看具体问题是什么)。⼆维的时候,我们可以横着一刀竖着一刀,分成左上、右上、左下、右下四份。而这道题的矩阵长度正好是 2^k,能够被不断平分下去。像是在暗示我们,要用分治,要用分治,要用分治…

当我们把整个区间按照中心点一分为四后,四个区间里面必然有一个区间有缺口(就是公主的位置),那这四个区间不一样,那就没有相同子问题了。别担心,只要我们在中心位置放上一块地毯,四个区间就都有一个缺口了。如下图所示:

在这里插入图片描述

无论缺口在哪里,我们都可以在缺口对应的区间的角落,放上一个地毯。接下来四个区间都变成只有一个缺口的形式,就可以用递归处理子问题。因此,我们拿到一个矩阵后的策略就是:

  1. 先四等分。
  2. 找出缺口对面的区间,放上一块地毯。
  3. 递归处理四个子问题。
#include<iostream>
using namespace std;int k, x, y;//len: 区间的长度; (a, b): 区间的左上角坐标; (x, y): 障碍物的坐标
void dfs(int len, int a, int b, int x, int y)
{if(len == 1) return;len /= 2;if(x < a + len && y < b + len) //障碍物在左上角{cout << a + len << " " << b + len << " " << 1 << endl; //铺上1号地毯dfs(len, a, b, x, y);dfs(len, a, b + len, a + len - 1, b + len);dfs(len, a + len, b, a + len, b + len - 1);dfs(len, a + len, b + len, a + len, b + len);}else if(x >= a + len && y >= b + len) //障碍物在右下角{cout << a + len - 1 << " " << b + len - 1 << " " << 4 << endl; //铺上4号地毯dfs(len, a, b, a + len - 1, b + len - 1);dfs(len, a, b + len, a + len - 1, b + len);dfs(len, a + len, b, a + len, b + len - 1);dfs(len, a + len, b + len, x, y);}else if(x >= a + len) //障碍物在左下角{cout << a + len - 1 << " " << b + len << " " << 3 << endl; //铺上3号地毯dfs(len, a, b, a + len - 1, b + len - 1);dfs(len, a, b + len, a + len - 1, b + len);dfs(len, a + len, b, x, y);dfs(len, a + len, b + len, a + len, b + len);}else //障碍物在右上角{cout << a + len << " " << b + len - 1 << " " << 2 << endl; //铺上2号地毯dfs(len, a, b, a + len - 1, b + len - 1);dfs(len, a, b + len, x, y);dfs(len, a + len, b, a + len, b + len - 1);dfs(len, a + len, b + len, a + len, b + len);}
}int main()
{cin >> k >> x >> y;k = (1 << k);dfs(k, 1, 1, x, y);return 0;
}

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

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

相关文章

MongoDB 数据库备份和恢复全攻略

在当今数据驱动的时代&#xff0c;数据库的稳定运行和数据安全至关重要。MongoDB 作为一款流行的 NoSQL 数据库&#xff0c;以其灵活的文档模型和高扩展性备受青睐。然而&#xff0c;无论数据库多么强大&#xff0c;数据丢失的风险始终存在&#xff0c;因此掌握 MongoDB 的备份…

Vue2官网教程查漏补缺学习笔记 - 3Vue实例4模板语法5计算属性监听器

3 Vue实例 3.1 创建一个 Vue 实例 每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的&#xff1a; var vm new Vue({// 选项 })虽然没有完全遵循 MVVM 模型&#xff0c;但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变…

LabVIEW橡胶动态特性测试系统

本文介绍了一个利用LabVIEW软件和NI高速数据采集设备构建的橡胶动态特性测试系统。该系统实现了橡胶材料动态性能的精确测量&#xff0c;并通过虚拟仪器技术&#xff0c;提高了测试数据的处理效率和准确性。系统支持实时数据处理和多种信号的动态分析&#xff0c;适用于工业和科…

(长期更新)《零基础入门 ArcGIS(ArcMap) 》实验六----流域综合处理(超超超详细!!!)

流域综合处理 流域综合治理是根据流域自然和社会经济状况及区域国民经济发展的要求,以流域水流失治理为中心,以提高生态经济效益和社会经济持续发展为目标,以基本农田优化结构和高效利用及植被建设为重点,建立具有水土保持兼高效生态经济功能的半山区流域综合治理模式。数字高程…

IOS 安全机制拦截 window.open

摘要 在ios环境&#xff0c;在某些情况下执行window.open不生效 一、window.open window.open(url, target, windowFeatures) 1. url&#xff1a;「可选参数」&#xff0c;表示你要加载的资源URL或路径&#xff0c;如果不传&#xff0c;则打开一个url地址为about:blank的空…

Java stream流的避坑指南

在使用Java Stream API时&#xff0c;虽然它提供了强大的功能来简化集合操作&#xff0c;但也存在一些常见的“坑”需要注意。以下是详细的避坑指南&#xff1a; 1. Stream的不可重用性 问题&#xff1a;Stream一旦被消费&#xff08;如调用forEach、collect等终端操作&#…

对于RocksDB和LSM Tree的一些理解

LSM Tree的读写过程 HBase、LevelDB&#xff0c;rocksDB&#xff08;是一个引擎&#xff09;底层的数据结构是LSM Tree适合写多读少的场景&#xff0c;都是追加写入内存中的MemTable&#xff0c;写入一条删除&#xff08;或修改&#xff09;标记&#xff0c;而不用去访问实际的…

偏差(Bias)和方差(Variance)

在机器学习中&#xff0c;偏差&#xff08;Bias&#xff09;和方差&#xff08;Variance&#xff09;是模型预测误差的两个主要组成部分&#xff0c;它们描述了模型在训练和预测过程中可能出现的两种不同类型的错误。 偏差&#xff08;Bias&#xff09; 偏差指的是模型在训练…

枚举与模拟 练习

练习题基于《C/C程序设计竞赛真题实战特训教程&#xff08;图解版&#xff09;》 目录 1.1 卡片 题目描述 代码实现 题解笔记 总评 注意点 重点解释 1.2 回文日期 题目描述 输入描述 输出描述 代码实现 题解笔记 总评 注意点 重点解释 1.1 卡片 题目描述 小蓝…

99.17 金融难点通俗解释:归母净利润

目录 0. 承前1. 简述2. 比喻&#xff1a;小明家的小卖部2.1 第一步&#xff1a;计算收到的所有钱2.2 第二步&#xff1a;减去各种支出2.3 第三步&#xff1a;计算能带回家的钱 3. 生活中的例子3.1 好的经营情况3.2 一般的经营情况3.3 不好的经营情况 4. 小朋友要注意4.1 为什么…

[LeetCode] 字符串 I — 344#反转字符串 | 541#反转字符串II | 54K替换数字

字符串 基础知识344# 反转字符串541# 反转字符串II54K 替换数字 基础知识 字符串的结尾&#xff1a;空终止字符00 char* name "hello"; // 字符串不可拓展&#xff08;由于是一个固定分配的内存块&#xff09;&#xff0c;有些地方必须加const char name2[5] {h,…

【深度学习|迁移学习】渐进式学习策略 (Progressive Learning Strategy)详述(一)

【深度学习|迁移学习】渐进式学习策略 (Progressive Learning Strategy)详述&#xff08;一&#xff09; 【深度学习|迁移学习】渐进式学习策略 (Progressive Learning Strategy)详述&#xff08;一&#xff09; 文章目录 【深度学习|迁移学习】渐进式学习策略 (Progressive L…

NIO 和 Netty 在 Spring Boot 中的集成与使用

Netty到底是个啥&#xff0c;有啥子作用 1. Netty 的本质&#xff1a;对 NIO 的封装 NIO 的原生问题&#xff1a; Java 的 NIO 提供了非阻塞 I/O 和多路复用机制&#xff0c;但其使用较为复杂&#xff08;如 Selector、Channel、Buffer 的配置和管理&#xff09;。开发者需要自…

Linux第103步_了解I2C总线框架

了解Linux中的I2C总线框架为后面做I2C实验做准备&#xff0c;学驱动&#xff0c;就是学习框架&#xff0c;了解是必须的。 1、了解Linux下的I2C子系统中的相关数据结构 struct i2c_adapter { struct module *owner; unsigned int class; /* classes to allow probing for …

AAAI2024论文合集解读|Physics-Informed Representation and Learning Control and Risk

论文标题 Physics-Informed Representation and Learning: Control and Risk Quantification 物理信息表征与学习&#xff1a;控制与风险量化 论文链接 Physics-Informed Representation and Learning: Control and Risk Quantification论文下载 论文作者 Zhuoyuan Wang, …

Vue3组件重构实战:从Geeker-Admin拆解DataTable的最佳实践

一、前言 背景与动机 在当前的开发实践中&#xff0c;我们选择了开源项目 Geeker-Admin 作为前端框架的二次开发基础。其内置的 ProTable.vue 组件虽然提供了一定程度的开箱即用性&#xff0c;但在实际业务场景中逐渐暴露出设计上的局限性&#xff0c;尤其是其将 搜索条件表单…

【JavaEE进阶】Spring留言板实现

目录 &#x1f38d;预期结果 &#x1f340;前端代码 &#x1f384;约定前后端交互接口 &#x1f6a9;需求分析 &#x1f6a9;接口定义 &#x1f333;实现服务器端代码 &#x1f6a9;lombok介绍 &#x1f6a9;代码实现 &#x1f334;运行测试 &#x1f384;前端代码实…

HackTheBox靶机:Sightless;NodeJS模板注入漏洞,盲XSS跨站脚本攻击漏洞实战

HackTheBox靶机&#xff1a;Sightless 渗透过程1. 信息收集常规探测深入分析 2. 漏洞利用&#xff08;CVE-2022-0944&#xff09;3. 从Docker中提权4. 信息收集&#xff08;michael用户&#xff09;5. 漏洞利用 Froxlor6. 解密Keepass文件 漏洞分析SQLPad CVE-2022-0944 靶机介…

Ansible入门学习之基础元素介绍

一、Ansible目录结构介绍 1.通过rpm -ql ansible获取ansible所有文件存放的目录 有配置文件目录 /etc/ansible/ 执行文件目录 /usr/bin/ 其中 /etc/ansible/ 该文件目录的主要功能是 inventory主机信息配置&#xff0c;ansible工具功能配置。 ansible自身的配置文件…

【pytorch 】miniconda python3.11 环境安装pytorch

ubuntu24.04 miniconda python3.11 环境安装pytorch 组件:langgraph本身不需要有一些模型是需要的:python3.11环境:报错ModuleNotFoundError: No module named ‘torchaudio’ ModuleNotFoundError: No module named ‘torchaudio’File "/root/miniconda3/envs/05_ep_…