草庐IT

一、基础算法(快排,归并,二分,高精度,前缀和,差分)

cdu-future 2023-03-28 原文

一、基础算法

快速排序

题目:给定你一个长度为 n的整数数列。

请你使用快速排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

数据范围:1≤n≤100000,所有整数均在 1∼10^9 范围内

#include <cstdio>  //数据比较大时,尽量用scanf,printf进行输入输出
#include <iostream>
using namespace std;     //swap函数需要std
const int N = 100010;
int n;
int a[N];
void quick_sort(int a[],int l,int r)
{
    if(l >= r) return;  //数组里只有1个或者没有数时返回
    int x = a[(l + r) / 2],i = l - 1,j = r + 1;  //数据加强,x只能取中间值,不能取两边值,否则超时
    while(i < j)
    {
        do i ++;while(a[i] < x);  //小于x即可,不需要小于等于
        do j --;while(a[j] > x);
        if(i < j) swap(a[i],a[j]);
    }
    quick_sort(a,l,j);   //如果取i的话,x取值需为(l + r + 1) / 2
    quick_sort(a,j + 1,r);
}
int main()
{
    scanf("%d",&n);
    for(int i = 0;i < n;i ++) scanf("%d",&a[i]);
    quick_sort(a,0,n - 1);
    for(int i = 0;i < n;i ++)printf("%d ",a[i]);
    return 0;
}

题目:给定一个长度为 n的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k个数。

数据范围:1≤n≤100000,所有整数均在 1∼10^9 范围内

#include <iostream>
using namespace std;
const int N = 100010;
int a[N];
int n,k;
int quick_sort(int l,int r,int k)  //k为第k个小的数
{
    if(l >= r)return a[l]; 
    int x = a[l + r >> 1];
    int i = l - 1;
    int j = r + 1;
    while(i < j)
    {
        while(a[ ++ i] < x);
        while(a[ -- j] > x);
        if(i < j)swap(a[i],a[j]);
    }
    int sl = j - l + 1;  //sl为左半边有多少个数
    if(sl >= k) return quick_sort(l,j,k);   //递归左半边
    else return quick_sort(j + 1,r,k - sl); //递归右半边,k-sl为右半边第k-sl个小的数
}
int main()
{
    cin >> n >> k;
    for(int i = 0;i < n;i ++ )scanf("%d",&a[i]);
    cout << quick_sort(0,n -1,k);
    return 0;
}

归并排序

题目:给定你一个长度为 n的整数数列。

请你使用归并排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

数据范围:1≤n≤100000,所有整数均在 1∼10^9 范围内

#include <cstdio>
#include <iostream>
using namespace std;
const int N = 100010;
int a[N];
int tmp[N];
int n;

void merge_sort(int a[],int l,int r)
{
    if(l >= r)return;    
    int mid = l + r >> 1;   //取中间值
    merge_sort(a,l,mid);
    merge_sort(a,mid + 1,r); //递归处理左右两边
    int i = l,j = mid + 1;   //指针指向左右两边起始处
    int k = 0 ;     //k为结果数组中已排好序的数量
    while(i <= mid && j <= r)
    {
        if(a[i] < a[j]) tmp[k ++] = a[i ++];
        else tmp[k ++] = a[j ++];
    }
    while(i <= mid) tmp[k ++] = a[i ++];
    while(j <= r) tmp[k ++] = a[j ++];   //如果有数组还剩余数,直接补到结果数组后

    for(int i = l,j = 0;i <= r;i ++ ,j ++)  //l到r为闭区间,将结果数组复制回去
    {
        a[i] = tmp[j];
    }

}
int main()
{
    cin >> n;
    for(int i = 0;i < n;i ++ )scanf("%d",&a[i]);
    merge_sort(a,0,n - 1);
    for(int i = 0;i < n;i ++ )printf("%d ",a[i]);
    return 0;
}

题目:给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。

数据范围:1≤n≤100000,所有整数均在 1∼10^9 范围内

#include <iostream>
using namespace std;
const int N = 100010;
int n,k;
int a[N],tmp[N];
typedef long long LL;   //数据超过int存储范围,需用long long
LL merge_sort(int l,int r)
{
    if(l >= r)return 0;
    int mid = l + r >> 1;
    int i = l,j = mid + 1;
    LL res = merge_sort(l,mid) + merge_sort(mid + 1,r); //递归处理左右两边
    k = 0;  //每次递归前k需要清零
    while(i <= mid && j <= r)
    {
        if(a[i] <= a[j])tmp[k ++ ] = a[i ++ ];
        else 
        {
            tmp[k ++ ] = a[ j ++ ];
            res += mid - i + 1;    //如果左边的数比右边大,那么左边这个数到中间的数都能形成逆序
        }
    }
    while(i <= mid)tmp[k ++ ] = a[i ++ ];
    while(j <= r)tmp[k ++ ] = a[ j ++ ];
    for(int i = l,j = 0;i <= r;i ++ ,j ++ )
        a[i] = tmp[j];
        return res;
}

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

二分

题目:给定一个按照升序排列的长度为 n的整数数组,以及 q个查询。

对于每个查询,返回一个元素 k的起始位置和终止位置(位置从 0开始计数)。

如果数组中不存在该元素,则返回 -1 -1

数据范围:1≤n≤100000,1≤q≤10000,1≤k≤10000

#include <iostream>
using namespace std;
const int N = 100010;
int a[N];
int n,q;
int main()
{
    cin >> n >> q;
    for(int i = 0;i < n;i ++ )
        scanf("%d",&a[i]);
    while(q --)
    {
        int k;
        scanf("%d",&k);
        int l = 0,r = n - 1;  //有边界不包括n
        
        while(l < r)
        {
            int mid = l + r >> 1;   //每次改变区间后mid需要重新计算,所以mid放入循环中
            if(a[mid] >= k) r = mid; //取等号是因为答案有可能取到边界
            else l = mid + 1;
        }
        if(a[l] != k) cout << "-1 -1" << endl;
        else 
        {
            cout << l << " ";
            int l = 0,r = n - 1;
           
            while(l < r)
            { 
                int mid = l + r + 1 >> 1;
                if(a[mid] <= k) l = mid;
                else r = mid - 1;
            }
            cout << l << endl;
        }
    }
    return 0;
}

题目:给定一个浮点数 n,求它的三次方根。

数据范围:-10000≤n≤10000,结果保留6位小数。

#include <iostream>    
using namespace std;
int main()
{
    double n;//注意浮点数
    cin >> n;
    double l = -10000,r = 10000;//注意浮点数
    while(r - l > 1e-8)  //注意10的-8次方,注意是大于
    {
        double mid = (l + r) / 2;//注意浮点数
        if(mid * mid * mid >= n)r = mid;
        else l = mid;
    }
    printf("%.6lf",l);
    return 0;
}

高精度

题目:给定两个正整数(不含前导 0),计算它们的和。

数据范围:1≤整数长度≤100000

#include <iostream>
#include <vector>
using namespace std;
vector<int> add(vector<int> &A,vector<int> &B)//引用的作用是少一遍数组拷贝,效率更高
{
    if(A.size() < B.size())return add(B,A);  //保证被加数更长
    vector<int> C;
    int t = 0;  //进位
    for(int i = 0;i < A.size();i ++ )
    {
        t += A[i];
        if(i < B.size())t += B[i];  //如果B还有数才加,因为有可能B比A更短
        C.push_back(t % 10); //存储个位
        t /= 10;  //存储进位
    }
    if(t) C.push_back(t);  //如果最后有进位,就补到最后一位
    return C;
}
int main()
{
    string a,b;
    vector<int> A,B;
    cin >> a >> b;
    
    for(int i = a.size() - 1;i >= 0;i -- )A.push_back(a[i] - '0');  //数据长,用vector存,倒着存
    for(int i = b.size() - 1;i >= 0;i -- )B.push_back(b[i] - '0');
    
    auto C = add(A,B);
    for(int i = C.size() - 1;i >= 0;i --)cout << C[i];  //由于是倒着存的,所以结果要倒着打
    cout << endl;
    return 0;
}

题目:给定两个正整数(不含前导 0),计算它们的差,计算结果可能为负数。

数据范围:1≤整数长度≤10^5

#include <iostream>
#include <vector>
using namespace std;
bool cmp(vector<int> A,vector<int> B)
{
    if(A.size() != B.size())return A.size() > B.size();
    for(int i = A.size() - 1 ;i >= 0;i --)  //倒着比
    {
        if(A[i] != B[i])return A[i] > B[i];
    }
    return true;  //两个数如果相等,返回真
}
vector<int> sub(vector<int> &A,vector<int> &B)//引用的作用是少一遍数组拷贝,效率更高
{
    vector<int> C;
    int t = 0;  //初始借位为0
    for(int i = 0;i < A.size();i ++)
    {
        t = A[i] - t;
        if(i < B.size()) t -= B[i];
        C.push_back((t + 10) % 10);  
        if(t < 0) t = 1;  //如果减出来为负数,则肯定有借位,所以把借位置为1,否则置为0
        else t = 0;
    }
    while(C.size() > 1 && C.back() == 0) C.pop_back();   //去掉前导0,如果刚好得0则不去掉这个0
    return C;
}
int main()
{
    string a,b;
    cin >> a >> b;
    vector<int> A,B;
    for(int i = a.size() - 1;i >= 0;i --)A.push_back(a[i] - '0');
    for(int i = b.size() - 1;i >= 0;i --)B.push_back(b[i] - '0');
    vector<int> C;
    if(cmp(A,B)) C = sub(A,B);  //比较哪个数更大,如果被减数更小,需要加负号,并计算B-A
    else 
    {
        C = sub(B,A);
        cout << "-";
    }
    
    for(int i = C.size() - 1;i >= 0;i --)
    {
        cout << C[i];
    }
    return 0;
}

题目:给定两个非负整数(不含前导 0) A和 B,请你计算 A×B 的值。

数据范围:1≤A的长度≤100000,0≤B≤10000

#include <iostream>
#include <vector>
using namespace std;
vector<int> mul(vector<int> &A,int b)  //引用的作用是少一遍数组拷贝,效率更高
{
    vector<int> C;
    int t = 0;
    for(int i = 0;i < A.size() || t;i ++)  //t为进位,需要等进位全部补入到最后才能结束循环
    {
        if(i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}
int main()
{
    string a; 
    int b;
    cin >> a >> b;
    vector<int> A;
    for(int i = a.size() - 1;i >= 0;i --)A.push_back(a[i] - '0');
    auto C = mul(A,b);
    for(int i = C.size() - 1;i >= 0;i --)printf("%d",C[i]);
    return 0;
}

题目:给定两个非负整数(不含前导 0) A,B,请你计算 A/B 的商和余数。

数据范围:1≤A的长度≤100000,0≤B≤10000,B 一定不为 0

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> dev(vector<int> &A,int b,int &r) //第一个引用的作用是少一遍数组拷贝,效率更高,第二个引用是将r的值带回去
{
    vector<int> C;
    r = 0;
    for(int i = A.size() - 1;i >= 0;i --)  //除法是倒着开始,因为商第一位的时候是从高位开始算的
    {
        r = r * 10 +A[i];
        C.push_back(r / b);
        r %= b;
    }
    reverse(C.begin(),C.end());    //翻转回来后便于去掉前导0
    while(C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}
int main()
{
    string a;
    int b;
    cin >> a >> b;
    vector<int> A;
    for(int i = a.size() - 1;i >= 0;i -- )A.push_back(a[i] - '0');
    int r;
    auto C = dev(A,b,r);
    for(int i = C.size() - 1;i >= 0;i --)printf("%d",C[i]);
    cout << endl;
    cout << r;
    return 0;
}

前缀和

输入一个长度为 n的整数序列。

接下来再输入 m个询问,每个询问输入一对 l,r。

对于每个询问,输出原序列中从第 l个数到第 r个数的和。

数据范围:1≤l≤r≤n,1≤n,m≤100000,−1000≤数列中元素的值≤1000

#include <iostream>
using namespace std;
const int N = 100010;
int a[N],s[N];
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i = 1;i <= n;i ++)scanf("%d",&a[i]);
    for(int i = 1;i <= n;i ++)s[i] = s[i - 1] + a[i];  //前缀和
    while(m --)
    {
        int l,r;
        cin >> l >> r;
        printf("%d",s[r] - s[l-1]);  
        cout << endl;
    }
    return 0;
}

题目:输入一个 n行 m列的整数矩阵,再输入 q个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。

对于每个询问输出子矩阵中所有数的和。

数据范围:1≤n,m≤1000,1≤q≤200000,1≤x1≤x2≤n,1≤y1≤y2≤m,−1000≤矩阵内元素的值≤1000

#include <iostream>
using namespace std;
const int N = 1010;
int a[N][N],s[N][N];
int main()
{
    int n,m,q;
    cin >> n >> m >> q;
    for(int i = 1;i <= n;i ++)   //下标是从1开始的
    {
        for(int j = 1;j <= m;j ++)
        scanf("%d",&a[i][j]);
    }
    for(int i = 1;i <= n;i ++)
    {
        for(int j = 1;j <= m;j ++)
            s[i][j] = s[i][j - 1] + s[i - 1][j] -s[i - 1][j - 1] + a[i][j];
    }
    while(q --)
    {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        printf("%d\n",s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 -1][y1 -1]);
    }
    
    
    return 0;
}

差分

题目:输入一个长度为 n的整数序列。

接下来输入 m个操作,每个操作包含三个整数 l,r,c,表示将序列中l,r之间的每个数加上 c。

请你输出进行完所有操作后的序列。

数据范围:1≤n,m≤100000,1≤l≤r≤n,−1000≤c≤1000,−1000≤整数序列中元素的值≤1000

#include <iostream>
using namespace std;
const int N = 100010;
int a[N],b[N];
void insert(int l,int r,int c)
{
    b[l] += c;
    b[r + 1] -= c;
}
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i = 1;i <= n;i ++)scanf("%d",&a[i]);
    for(int i = 1;i <= n;i ++)insert(i,i,a[i]);
    while(m --)
    {
        int l,r,c;
        scanf("%d%d%d",&l,&r,&c);
        insert(l,r,c);
    }
    for(int i = 1;i <= n;i ++)
    {
        b[i] += b[i - 1] ;
    }
    for(int i = 1;i <= n;i ++)printf("%d ",b[i]);
    return 0;
}

题目:输入一个 n 行 m列的整数矩阵,再输入 q 个操作,每个操作包含五个整数 x1,y1,x2,y2,c,其中 (x1,y1)和 (x2,y2)表示一个子矩阵的左上角坐标和右下角坐标。

每个操作都要将选中的子矩阵中的每个元素的值加上 c。

请你将进行完所有操作后的矩阵输出。

数据范围:1≤n,m≤1000,1≤q≤100000,1≤x1≤x2≤n,1≤y1≤y2≤m,−1000≤c≤1000,−1000≤矩阵内元素的值≤1000

#include <iostream>
using namespace std;
const int N = 1010;
int a[N][N],b[N][N];
void insert(int x1,int y1,int x2,int y2,int c)
{
    b[x1][y1] += c;
    b[x2 + 1][y1] -= c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y2 + 1] +=c;
}
int main()
{
    int n,m,q;
    scanf("%d%d%d",&n,&m,&q);
    for(int i = 1;i <= n;i ++)
    {
        for(int j = 1;j <= m;j ++)
            scanf("%d",&a[i][j]);
    }
    for(int i = 1;i <= n;i ++)
    {
        for(int j = 1;j <= m;j ++)
            insert(i,j,i,j,a[i][j]);
    }
    while(q --)
    {
        int x1,y1,x2,y2,c;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
        insert(x1,y1,x2,y2,c);
    }
    for(int i = 1;i <= n;i ++)
    {
        for(int j = 1;j <= m;j ++)
            b[i][j] += b[i][j - 1] + b[i - 1][j] -b[i - 1][j - 1] ;
    }
    for(int i = 1;i <= n;i ++)
    {
        for(int j = 1;j <= m;j ++)
            printf("%d ",b[i][j]);
        cout << endl;
    }
    return 0;
}

更多内容请关注我的博客:bgemini.com

有关一、基础算法(快排,归并,二分,高精度,前缀和,差分)的更多相关文章

  1. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  2. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  3. 软件测试基础 - 2

    Ⅰ软件测试基础一、软件测试基础理论1、软件测试的必要性所有的产品或者服务上线都需要测试2、测试的发展过程3、什么是软件测试找bug,发现缺陷4、测试的定义使用人工或自动的手段来运行或者测试某个系统的过程。目的在于检测它是否满足规定的需求。弄清预期结果和实际结果的差别。5、测试的目的以最小的人力、物力和时间找出软件中潜在的错误和缺陷6、测试的原则28原则:20%的主要功能要重点测(eg:支付宝的支付功能,其他功能都是次要的)80%的错误存在于20%的代码中7、测试标准8、测试的基本要求功能测试性能测试安全性测试兼容性测试易用性测试外观界面测试可靠性测试二、质量模型衡量一个优秀软件的维度①功能性功

  4. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  5. [工业相机] 分辨率、精度和公差之间的关系 - 2

    📢博客主页:https://blog.csdn.net/weixin_43197380📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由Loewen丶原创,首发于CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览:一.分辨率(Resolution)1、工业相机的分辨率是如何定义的?2、工业相机的分辨率是如何选择的?二.精度(Accuracy)1、像素精度(PixelAccuracy)2、定位精度和重复定位精度(RepeatPrecision)三.公差(Tolerance)四.课后作业(Post-ClassExercises)视觉行业的初学者,甚至是做了1~2年

  6. ruby-on-rails - ruby on rails 模型验证中的浮点精度 - 2

    我正在尝试使用正则表达式验证美元金额:^[0-9]+\.[0-9]{2}$这工作正常,但每当用户提交表单并且美元金额以0(零)结尾时,ruby(或rails?)将0砍掉。所以500.00变成500.0,因此正则表达式验证失败。有没有办法让ruby​​/rails保持用户输入的格式,而不管尾随零? 最佳答案 我假设您的美元金额是小数类型。因此,用户在字段中输入的任何值在保存到数据库之前都会从字符串转换为适当的类型。验证适用于已转换为数字类型的值,因此在您的情况下,正则表达式并不是真正合适的验证过滤器。不过,您有几种可能性可以解决这个问

  7. 【网络】-- 网络基础 - 2

    (本文是网络的宏观的概念铺垫)目录计算机网络背景网络发展认识"协议"网络协议初识协议分层OSI七层模型TCP/IP五层(或四层)模型报头以太网碰撞路由器IP地址和MAC地址IP地址与MAC地址总结IP地址MAC地址计算机网络背景网络发展        是最开始先有的计算机,计算机后来因为多项技术的水平升高,逐渐的计算机变的小型化、高效化。后来因为计算机其本身的计算能力比较的快速:独立模式:计算机之间相互独立。    如:有三个人,每个人做的不同的事物,但是是需要协作的完成。    而这三个人所做的事是需要进行协作的,然而刚开始因为每一台计算机之间都是互相独立的。所以前面的人处理完了就需要将数据

  8. 100个python算法超详细讲解:画直线 - 2

    1.问题描述使用Python的turtle(海龟绘图)模块提供的函数绘制直线。2.问题分析一幅复杂的图形通常都可以由点、直线、三角形、矩形、平行四边形、圆、椭圆和圆弧等基本图形组成。其中的三角形、矩形、平行四边形又可以由直线组成,而直线又是由两个点确定的。我们使用Python的turtle模块所提供的函数来绘制直线。在使用之前我们先介绍一下turtle模块的相关知识点。turtle模块提供面向对象和面向过程两种形式的海龟绘图基本组件。面向对象的接口类如下:1)TurtleScreen类:定义图形窗口作为绘图海龟的运动场。它的构造器需要一个tkinter.Canvas或ScrolledCanva

  9. ruby - Ruby 的任意精度算术 - 2

    Ruby到底是怎么做到的?Jörg或其他人是否知道幕后发生的事情?不幸的是,我不太了解C,所以bignum.c对我帮助不大。我只是有点好奇有人可以解释(用简单的英语)它使用的任何神奇算法背后的理论。irb(main):001:0>999**99936806348825922326789470084006052186583833823203735320465595962143702560930047223153010387361450517521869134525758989639113039318944796977164583238219236607653663113200177617

  10. ruby - 在 Ruby 中实现 Luhn 算法 - 2

    我一直在尝试用Ruby实现Luhn算法。我一直在执行以下步骤:该公式根据其包含的校验位验证数字,该校验位通常附加到部分帐号以生成完整帐号。此帐号必须通过以下测试:从最右边的校验位开始向左移动,每第二个数字的值加倍。将乘积的数字(例如,10=1+0=1、14=1+4=5)与原始数字的未加倍数字相加。如果总模10等于0(如果总和以零结尾),则根据Luhn公式该数字有效;否则无效。http://en.wikipedia.org/wiki/Luhn_algorithm这是我想出的:defvalidCreditCard(cardNumber)sum=0nums=cardNumber.to_s.s

随机推荐