常用代码模板2——基础算法

news2025/2/28 20:08:13

构思算法:可以先想暴力解法,然后观察时间复杂度,如果超时,再考虑优化,优化的方向就是时间复杂度要下降,下表可以给出一些算法选择的参考:

暴力枚举 -> 枚举+优化 -> 正解

数据范围时间复杂度考察的算法
n \leq 30n≤30指数级别dfs+剪枝,状态压缩dp
n\leq10^2n≤102O(n^3)O(n3)floyd,dp
n\leq10^3n≤103O(n^2),O(n^2\log n)O(n2),O(n2logn)dp,二分,朴素版Dijkstra,朴素版Prim,Bellman-Ford
n\leq10^4n≤104O(n*\sqrt{n})O(n∗n​)块状链表,分块,莫队
n\leq10^5n≤105O(n\log n)O(nlogn)各种sort,线段树,树状数组,set/map,heap,拓扑排序,dijkstra+heap,prim+heap,spfa,二分
n\leq10^6n≤106O(n)O(n),常数较小的 O(n\log n)O(nlogn)算法hash,双指针扫描,并查集,常数较小的 O(n\log n)O(nlogn)算法:sort,树状数组,ST表,heap,dijkstra,spfa
n\leq10^7n≤107O(n)O(n)双指针扫描,线性筛素数
n\leq10^9n≤109O(\sqrt{n})O(n​)判断质数
n\leq10^{18}n≤1018O(\log n)O(logn)最大公约数,快速幂
n\leq10^{1000}n≤101000O((\log n)^2)O((logn)2)高精度加减乘除

备注:\log nlogn 主要出现在二分、分治和树、图中。

素数判定

试除法

// 试除法:判断一个整数 i 是不是素数,是则返回 true,否则返回 false
bool isprime(int i){
    if(i < 2)
        return false;
    for(int j = 2; j <= sqrt(i); j++)
        if(i % j == 0)
            return false;
    return true;
}

Copy

埃氏筛法

const int N = 100000;   // N 的大小取决于问题中 n 的范围
bool a[N];   // 标记数组  a[i] = 0(false):素数,a[i]=1(true): 非素数
// 埃氏筛法 函数执行完后 a[] 数组即为筛出的素数结果 a[i]=false 表示 i 是素数
void prime()
{
    a[0]=a[1]=true;         // 0 和 1 不是素数
    for(int i = 2; i <= n; i++)
    {
        if(a[i] == false)   // i 是素数
        {
            for(int j = i+i; j <= n; j += i)
                a[j] = true;
        }
    }
}

Copy

例题:素数距离 - TopsCoding

质因数分解

int t = 2;
while(k!=1){
    while(k%t == 0) {
        k /= t;
        cout << t << ' ';
    }

    t++;
}

Copy

递推

数学中的斐波那契数列、错排问题的递推公式、等差数列、等比数列问题,都可以用递推去解决。

在编程中,计算问题的解时,如果可以找到前后过程之间的数量关系(即递推式),那么就可以用递推去解决。
f[i] = g(f[i-1],f[i-2],...,f[1])f[i]=g(f[i−1],f[i−2],...,f[1])

int f[N] = {部分初始值};
for(int i = 2; i <= n; i++)
{
    f[i] = ...f[i-1]...f[i-2]...;
}
cout << f[n];

Copy

例题:小 C 的数组(array) - TopsCoding

逆序对

冒泡排序

时间复杂度:O(n^2)O(n2)

适用场景:数据范围不大时,可用来求逆序对

模板题:求逆序对个数I - TopsCoding

int n, a[N], cnt = 0;
for(int i=0; i<n; i++)
{
    for(int j=0; j<n-i; j++)
    {
        if(a[j]>a[j+1]) {
            swap(a[j], a[j+1]);  //交换数据,同时也意味着存在一个逆序对
            cnt++;   // 逆序对++
        }
    }
}

Copy

归并排序

时间复杂度:O(n\log n)O(nlogn)

适用场景:数据范围较大时,可用来求逆序对

模板题:求逆序对个数II - TopsCoding

int n, a[N], tmp[N];   // tmp 数组用来保存中间排序结果
int cnt = 0;   // 逆序对个数

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

    int mid = l + r >> 1;
    merge_sort(l, mid);        // 排序左半部分
    merge_sort(mid + 1, r);    // 排序右半部分

    // 合并左右子数组
    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r) {
        if (a[i] <= a[j]) {
            tmp[k++] = a[i++]; 
        }
        else {
            tmp[k++] = a[j++];
            cnt += mid-i+1; // a[i]>a[j],必然有a[i]~a[mid]>a[j],有 mid-i+1 对
        }
    }
    while (i <= mid) tmp[k++] = a[i++];
    while (j <= r) tmp[k++] = a[j++];
    for (i = l, j = 0; i <= r; i++, j++) a[i] = tmp[j];
}

Copy

快速幂*

求 m^k%pmk,时间复杂度 O(\log k)O(logk)。

// 递归写法
int qmi(int m, int k, int p) {
    if (k == 0) return 1;
    if (k & 1) {
        return m * qmi(m, k-1, p) % p;
    }
    else {
        int t = qmi(m, k>>1, p);
        return t * t % p;
    }
}

// 迭代写法
int qmi(int m, int k, int p)
{
    int res = 1 % p, t = m;
    while (k)
    {
        if (k&1) res = res * t % p;
        t = t * t % p;
        k >>= 1;
    }
    return res;
}

Copy

二分答案

int l = MIN, r = MAX, mid, ans;
while(l <= r) {
    mid = (l + r) / 2;
    if(check(mid)) {
        ans = mid;      // 记录当前最优解
        l = mid + 1;    // 看是找哪边,继续去搜 左半/右半 区间
        // or r = mid - 1;
    } else {
        r = mid - 1;
        // or l = mid + 1;
    }
}
cout << ans << endl;

Copy

例题:

  1. 木板打地鼠 - TopsCoding
  2. 游戏闯关 - TopsCoding

一维前缀和

二维前缀和

贪心

所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。 也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。

贪心算法一般按如下步骤进行:①建立数学模型来描述问题。②把求解的问题分成若干个子问题。③对每个子问题求解,得到子问题的局部最优解。④把子问题的解局部最优解合成原来解问题的一个解。

一般会用到排序,或优先队列,没有固定的代码模板。

区间贪心

区间类贪心一般会要求对若干个区间进行筛选。解题思路一般为:

  1. 先对区间按照左端点从小到大,右区间从大到小进行排序(方便对区间进行遍历);
  2. 遍历区间,贪心选取一些区间,一般会用到指针变量。

例题:区间覆盖 - TopsCoding

BFS

即按层逐个遍历,适合求解最短路类问题,尤其是图上的最短路问题(最短路类问题用 dfs 在大数据量下很容易超时)。代码框架如下:

全局状态变量

void bfs(初始状态)
{
    定义状态队列
    初始状态入队
    while(队列不为空)
    {
        取出队首状态作为当前状态
        if(当前状态是目标状态) {
            进行相应处理(输出当前解、更新最优解、退出返回等) 
            return ;
        }
        for(所有可行的新状态)   
        {
            if(新状态没有访问过 && 需要访问)  // 可行性剪枝、最优性剪枝、重复性剪枝
            {
                新状态入队
            }
        }
}

Copy

练习题:

  1. 抓牛(catchcow) - TopsCoding

  2. 瓷砖 - TopsCoding

  3. 单词接龙 II - TopsCoding

  4. 老子的意大利炮呢 - TopsCoding

DFS

全局状态变量

void/int dfs(当前状态)
{
    if(当前状态是目标状态)    // 判断
    {
         进行相应处理(输出当前解、更新最优解、退出返回等)   
    }
    for(所有可行的新状态)   // 扩展
    {
        if(新状态没有访问过 && 需要访问)  // 可行性剪枝、最优性剪枝、重复性剪枝
        {
            标记 
            dfs(新状态);
            取消标记       // 回溯
        }
    }
}
                
int main()
{
    ...
    dfs(初始状态);
    ...
}

Copy

易错点:需要注意的是,定义 dfs() 函数前一定要先考虑清楚,函数是否需要有返回值!如果定义了带返回值,但是函数中未写return,则会爆零!

剪枝

可行性剪枝:在搜索过程中,一旦发现如果某些状态无论如何都不能找到最终的解,就可以将其“剪枝”了,比如越界操作、非法操作。一般通过条件判断来实现,如果新的状态节点是非法的,则不扩展该节点

重复性剪枝:对于某一些特定的搜索方式,一个方案可能会被搜索很多次,这样是没必要的。在实现上,一般通过一个记忆数组来记录搜索到目前为止哪些状态已经被搜过了,然后在搜索过程中,如果新的状态已经被搜过了,则不再扩展该状态节点。

最优性剪枝:对于求最优解的一类问题,通常可以用最优性剪枝,比如在求解迷宫最短路的时候,如果发现当前的步数已经超过了当前最优解,那从当前状态开始的搜索都是多余的,因为这样搜索下去永远都搜不到更优的解。通过这样的剪枝,可以省去大量冗余的计算,避免超时。在实现上,一般通过一个记忆数组来记录搜索到目前为止的最优解,然后在搜索过程中,如果新的状态已经不可能是最优解了,那再往下搜索肯定搜不到最优解,于是不再扩展该状态节点。

练习题:

  1. 全排列问题 - TopsCoding
  2. 素数环 - TopsCoding
  3. 迷宫问题 - TopsCoding
  4. 细胞 - TopsCoding

记忆化搜索

每次搜索前先判断是否搜索过:

  1. 如果搜过,就直接返回之前存储的结果
  2. 否则搜索,并在返回之前将当前状态保存下来
int f[][][];

int dfs(状态列表)
{
    if(f[][][]) return f[][][];    // 避免重复搜索
    if(边界状态) {
        return f[][][] = ......;
    }
    int ans = ...;
    for(新状态) {
        ans = max/min(dfs(...), ans);
    }
    return f[][][] = ans;     // 返回前先保存
}

Copy

拓展:记忆化搜索和递推型动态规划联系紧密。如果碰到一些动规类型的题目,想不出怎么写时,可以考虑用记忆化搜索去解决。

练习题:

  1. 滑雪 - TopsCoding
  2. 搭积木(block) - TopsCoding
  3. 汉诺塔 V - TopsCoding

高精度加法

模板题:A+B Problem(高精度加法)

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

string add(string a, string b)
{
    string ans;
    // 翻转,使个位在前
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    // 预处理,方便后面按位相加
    a.push_back('0');  b.push_back('0');
    if(a.size() < b.size()) swap(a, b);
    // 短的数字高位补零
    while(b.size() < a.size()) b.push_back('0');
    // 按位累加,模拟加法
    int c[1000]={0}, k = a.size();
    for(int i = 0; i < a.size(); i++)
    {
        int s = c[i] + a[i]-'0' + b[i] - '0';
        c[i] = s%10;
        c[i+1] += s/10;
    }
    // 去除高位零
    while(c[k]==0 && k>=0) k--;
    // 输出
    while(k >= 0)
        ans.push_back(c[k--]+'0');
    return ans;
}

int main()
{
    string a, b;
    cin >> a >> b;
    cout << add(a, b);
    return 0;
}

Copy

更简洁的写法:

string add(string a, string b) {
    string c;
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    int t = 0;
    for (int i = 0; i < a.size() || i < b.size(); i++) {
        if (i < a.size()) t += a[i] - '0';
        if (i < b.size()) t += b[i] - '0';
        c.push_back(t % 10 + '0');
        t /= 10;
    }
    if (t) c.push_back(t % 10 + '0');
    reverse(c.begin(), c.end());
    return c;
}

Copy

高精度减法

模板题:A-B Problem(高精度减法)

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

const int N = 1e6+5;

string sub(string a, string b)
{
    string ans;
    bool flag = false;
    if(a.size() < b.size() || (a.size()==b.size() && a < b))
    {
        swap(a, b);
        flag = true;    
    }
    // 翻转,使个位在前
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    // 预处理,方便后面按位相减
    a.push_back('0');  b.push_back('0');
    if(a.size() < b.size()) swap(a, b);
    // 短的数字高位补零
    while(b.size() < a.size()) b.push_back('0');
    // 按位相减,模拟减法
    int c[1000]={0}, k = a.size();
    for(int i = 0; i < a.size(); i++)
    {
        if(a[i] < b[i]) a[i]+=10, a[i+1]--;
        c[i] = a[i] - b[i];
    }
    // 去除高位零
    while(c[k]==0 && k>=0) k--;
    // 输出
    if(flag) ans.push_back('-');
    while(k >= 0)
        ans.push_back(c[k--]+'0');
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    string a, b;
    cin >> a >> b;
    string c = sub(a,b);
    cout << c;
    return 0;
}

Copy

高精度乘法

模板题:A*B Problem(高精度乘法)

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

const int N = 1e6+5;

string div(string a, string b)
{
    string ans;
    // 翻转,使个位在前
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    // 按位相乘,模拟乘法
    int c[1000]={0}, k = a.size()+b.size()+1;
    for(int i = 0; i < a.size(); i++)
        for(int j = 0; j < b.size(); j++)
        {
            c[i+j] += (a[i]-'0')*(b[j]-'0');  // 乘,写上去
            c[i+j+1] += c[i+j]/10;      // 进位
            c[i+j] %= 10;       // 保留个位
        }
    // 去除高位零
    while(c[k]==0 && k>=0) k--;
    // 输出
    while(k >= 0)
        ans.push_back(c[k--]+'0');
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    string a, b;
    cin >> a >> b;
    string c = div(a,b);
    cout << c;
    return 0;
}

Copy

高精度除法

模板题:A/B Problem(高精度除法)

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

string div(string a, long long b)
{
    // z 数组保存计算结果
    int z[256] = {0}, d = 0;  // d 存余数
    // 计算相除
    for(int i = 0; i < a.size(); i++)
    {
        d = 10*d + a[i] - '0';   // 当前位拿下来
        z[i] = d / b;            // 商写上去
        d %= b;                  // 更新余数
    }
    
    // 处理计算结果
    string ans;   // 作为返回值
    int len = 0;
    while(z[len] == 0 && len < a.size()-1)    // 去掉高位的0
        len++;
    for(int i = len; i < a.size(); i++)
        // 将结果放到 ans 字符串中,如果
        ans.push_back(z[i]+'0');
    return ans;
}       

int main()
{
    string a;
    long long b;
    cin >> a >> b;
    cout << div(a, b);
    return 0;
}

Copy

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

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

相关文章

Qt扫盲-QTabWidget理论总结

QTabWidget理论总结1. 简述2. 用法流程3. 选项卡设置3. 页面信息3. 外观4. 扩展1. 简述 QTabWidget 提供一个选项卡栏和一个“页面区域”&#xff0c;用于显示与每个选项卡相关的页面。默认情况下&#xff0c;选项卡栏显示在页面区域上方&#xff0c;但可以使用不同的配置&…

智能合约简单介绍

本学期学习了区块链的课程&#xff0c;作业是对于智能合约学习后的报告&#xff1a; 1 智能合约简单了解 1.1智能合约是什么 智能合约是由事件驱动的、具有状态的、部署于可共享的分布式数据库上的计算机程序&#xff0c;多用IF-THEN语句。狭义来说&#xff0c;智能合约是设计…

函数式编程 | 图文详解 | 系统性学习 | 无知的我费曼笔记

无知的我正在复盘函数式编程 该笔记特点是 重新整理了涉及资料的一些语言描述、排版而使用了自己的描述对一些地方做了补充说明。比如解释专有名词、类比说明、对比说明、注意事项提升了总结归纳性。尽可能在每个知识点上都使用一句话 || 关键词概括更注重在实际上怎么应用提出…

70. 利用 ALV 实现增删改查系列之二:仅让 ALV 报表某一列允许被编辑

本系列前一篇文章 69. 利用 ALV 实现增删改查系列之一:让 ALV 报表进入可编辑状态,我们介绍了如何让 SAP ABAP ALV 报表进入可编辑状态。在该状态下,ALV 报表的每一行,每一列都可以被任意修改,如下图所示: 但是在实际的项目中,更常见的需求是,ALV 报表只允许部分列能够…

【Vue脚手架】总结笔记

脚手架文件结构 ├── node_modules ├── public │ ├── favicon.ico: 页签图标 │ └── index.html: 主页面 ├── src │ ├── assets: 存放静态资源 │ │ └── logo.png │ │── component: 存放组件 │ │ └── HelloWorld.vue │ │…

Springboot 整合 ireporter 实践

背景 这段时间&#xff0c;在做项目时&#xff0c;设计到需要带参数的批量生成报告。尝试了很多方法&#xff0c;包括grafana等BI工具。虽然grafana这种BI工具可以在线查看&#xff0c;但是无法导出。甚至当想把报告整合邮件发送时&#xff0c;grafana就显得无能为力了。于是在…

Sql Server 占用内存高,不释放内存问题处理记录(腾讯电脑管家小火箭清内存)

问题&#xff1a; 本人仓库管理系统厂家&#xff0c;项目上反应生产线箱码不能上传到我们系统&#xff0c;我们的WMS软件退出后重新登录也一直登录失败&#xff0c;并且服务器上数据库占用内存过高&#xff0c;SSMS数据库连接不上。 好家伙SQL Server占用了34个G内存。 接口…

前端基础(十二)_overflow属性、clear属性、vertical-align属性

overflow属性 overflow的第一个属性值是x轴方向的属性&#xff0c;第二个值是y周方向的属性 <style>.box1 {width: 150px;height: 50px;等价于下面两个属性/* overflow: hidden auto; */overflow-x: hidden;overflow-y: auto;}</style>1、overflow-x 只包含水平方…

VS2019编译QT6源码

现在QT在线安装只有QT6.2以上的版本和QT5.15&#xff0c;其他版本就需要自己编译&#xff0c;本文讲解在vs2019环境下编译qt6.1.3源码。 1 编译准备 从清华大学镜像站&#xff0c;下载源码&#xff0c;地址如下&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/qt/archive/…

【C++】继承/多态/文件

文章目录一、继承1 继承中的对象模型2 继承中构造和析构顺序3 继承中同名属性和函数处理方式&#xff08;隐藏&#xff09;4 继承同名静态成员属性和函数处理方式5 多继承语法6 菱形继承&#xff08;虚继承&#xff09;二、多态1 多态的原理剖析&#xff08;虚函数表指针&#…

【OpenSourceC#】JEngine框架

前言 一个完全依赖ILRuntime的框架&#xff0c;集成好了ILRuntime和AB资源热更&#xff0c;想用ILRuntime的可以很方便使用。 对着作者的文档JEngine看看框架功能都是怎么实现的。 看下文档中提到的功能如下 JEngine框架核心 热更资源工具 AssetMgr&#xff0c;框架集成…

询盘回复流程及外贸11步流程

询盘回复流程&#xff1a; 从事外贸的新手经常在网上寻找各种各样的外贸干货知识。其实&#xff0c;你不仅要关注某个环节的小细节&#xff0c;还要有“宏观”的思维。分过程、分步骤地梳理外贸工作&#xff0c;是外贸工作中十分重要的一个环节。今天&#xff0c;按照一般的回…

点云中点法向量,点拟合的直线,点拟合的平面

点云中点法向量 计算步骤&#xff1a; 找到点pi相邻点集合S所有点Vi,然后去中心化,并构造协方差矩阵,公式如下: 二维点云该点曲率计算方法&#xff1a; 三维点云该点曲率计算方法&#xff1a; 最小特征值对应的特征向量就是点的法向量 Eigen::Vector2d ComputeNormal(std::v…

m基于BP神经网络的房屋价值变换趋势预测matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 住宅价格是住宅市场的核心&#xff0c;住宅市场的变化关系到广大消费者的切身利益&#xff0c;商品房价格是升是降&#xff0c;销售是冷是旺&#xff0c;是社会关注的热点问题。因此&#xff0c;…

Mybatis-Plus 入门

文章目录0. 前言1. 相关概念1.1 名称介绍1.2 官网特性介绍2. 快速入门2.1 准备工作2.2.1 准备表2.2.2 创建工程2.2 入门测试2.2.1 编写实体类2.2.2 编写Mapper2.2.3 编写测试类3. CRUD3.1 条件构造器3.1.1 AbstractWrapper&#xff08;1&#xff09; 基本比较操作&#xff08;2…

想做数码管显示,单片机IO口资源不够?看看WTV890语音芯片能做些啥

在开发许多产品时许多工程师都会遇到以下问题&#xff0c;如&#xff1a;想驱动4位或10位的LED数码管显示&#xff0c;但是单片机的IO口不够用&#xff1b;要么更换IO口资源更多的MCU来控制&#xff0c;要么更换脚位丰富的单片机&#xff0c;这无疑在MCU上&#xff0c;加了1元左…

在Linux中使用at和crontab命令在指定时间计划任务服务程序

经验丰富的系统运维工程师可以使得Linux在无需人为介入的情况下&#xff0c;在指定的时间段自动启用或停止某些服务或命令&#xff0c;从而实现运维的自动化。尽管我们现在已经有了功能彪悍的脚本程序来执行一些批处理工作&#xff0c;但是&#xff0c;如果仍然需要在每天凌晨两…

2023年电气,电子与信息工程国际会议(ISEEIE 2023)

2023年电气&#xff0c;电子与信息工程国际会议&#xff08;ISEEIE 2023&#xff09; 重要信息 会议网址&#xff1a;www.iseeie.org 会议时间&#xff1a;2023年2月24-26日 召开地点&#xff1a;新加坡 截稿时间&#xff1a;2022年12月31日 录用通知&#xff1a;投稿后2周…

Mac如何做才能彻底清理垃圾

Mac磁盘空间又爆满了&#xff1f;系统运行又卡了&#xff1f;你的Mac需要清理内存缓存垃圾啦&#xff01;本文教会你如何彻底清除Mac垃圾文件&#xff0c;释放存储空间。 现在&#xff0c;Mac电脑最顶配的硬盘可达2TB。这么大的容量&#xff0c;应该够用了吧&#xff1f;可真正…

windowsserver2016安装

vmware安装windowsserver2016: 开始安装OpcServer服务器端&#xff1a; DOM配置过程&#xff1a; 1.设置OpcServer和OpcClient相同的administrator账号和密码 administrator、wong123 2.关闭OpcServer和OpcClient的防火墙 3.开启OpcServer和OpcClient远程桌面访问 4.OpcServe…