第一章 基础算法(二)

高精度

高精度加法

image-20220730162041560

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

输入格式
共两行,每行包含一个整数。

输出格式
共一行,包含所求的和。

数据范围
1≤整数长度≤100000
输入样例:
12
23
输出样例:
35
#include <iostream>
#include <vector>
using namespace std;
// C = A + B
vector<int> add(vector<int> &A,vector<int> &B)  // 引用:提升效率,不加引用需要拷贝
{
    vector<int> C;
    int t = 0;  //上一次进位,最终用t来存储最终结果
    for(int i = 0; i < A.size()|| i < B.size();i++)
    {
        if(i < A.size()) t+=A[i];  //从上一次进位的基础上累加
        if(i < B.size()) t+=B[i];
        //t表示该位上的总的结构,结果vector中需要插入的是模10后的余数
        C.push_back(t%10);
        t /= 10; //用于下一次累加
    }
    if(t) C.push_back(t);
    return C;
    
}

int main()
{
    string a,b; //用字符串读 123456
    vector<int> A,B,C;  //存储到vector容器中去
    cin>>a>>b;
    //逆序
    for(int i = a.size()-1;i>=0;i--) A.push_back(a[i]-'0'); // A 654321 
    for(int i = b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
    C = add(A,B);
    //倒着输出,先输出最高位,再输出次高位
    for(int i =C.size()-1;i>=0;i--) printf("%d",C[i]);
    return 0;
}

高精度减法

image-20220730163203860image-20220730163319188

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

输入格式
共两行,每行包含一个整数。

输出格式
共一行,包含所求的差。

数据范围
1≤整数长度≤105
输入样例:
32
11
输出样例:
21
#include <iostream>
#include <vector>
using namespace std;
//判断是否A>=B
bool cmp(vector<int> &A,vector<int> &B)
{
    //首先判断位数 判断大小只要不等直接返回A.size()>B.size()
    if(A.size()!= B.size()) return A.size()>B.size();
    // i--时,第二个判断条件为>=,i++时,第二个条件可以为 <
    for(int i = A.size()-1;i>=0;i--)
        if(A[i]!=B[i])
                //判断大小只要不等直接返回A.size()>B.size()
                return A[i]>B[i];
    return true;
}
vector<int> sub(vector<int> &A,vector<int> &B)
{
    vector<int> C;
    for(int t = 0,i = 0;i < A.size();i++)
    {
        //t 上次运算的借位
        t = A[i] - t;
        if(i < B.size()) t -=B[i];
        // t>=0 -- t  t<0 ---t+10
        C.push_back((t+10)%10);
        if(t < 0) t = 1;
        else t = 0;
    }
    //清空前置0,如果本身是0,不进行前导0 即C.size()>1
    while(C.size()>1 && C.back() == 0) C.pop_back();

    return C;
}

int main()
{
    string a,b;
    vector<int> A,B,C;
    cin>>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');
    if(cmp(A,B))
    {
        C = sub(A,B);
        
    }
    else
    {
        C = sub(B,A);
        printf("-");
    }
    for(int i =C.size()-1;i>=0;i--) printf("%d",C[i]);


    return 0;
}

高精度乘法

image-20220730164424097

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

输入格式
共两行,第一行包含整数 A,第二行包含整数 B。

输出格式
共一行,包含 A×B 的值。

数据范围
1≤A的长度≤100000,
0≤B≤10000
输入样例:
2
3
输出样例:
6
#include <iostream>
#include <vector>
using namespace std;


//C = A * b
vector<int> mul(vector<int> &A,int b)
{
    vector<int> C;
    int t = 0; //进位
    //需要判断两种情况 A的位数没有处理完  或者  进位没有处理完
    for(int i = 0;i<A.size()||t;i++)
    {
        if(i<A.size()) t += A[i]*b;
        //插入模10后的余数
        C.push_back(t%10);
        t/=10;
    }
    while(C.size() > 1&& C.back() == 0) C.pop_back();
    return C;
}

/*
如果将for循环两个条件拆开,可以采用这种写法,但和在一块写效果更好
vector<int > mul(vector<int > &a,int &b)
{
    int t = 0;
    for(int i = 0;i < a.size();i++)
    {
        t += a[i]*b;
        c.push_back(t%10);
        t /=10;
    }
    while(t) 
    {
        c.push_back(t%10);
        t /= 10;
    }
    while(c.size() > 1 && c.back() == 0) c.pop_back();
    return c;
}

*/

int main()
{
    string a;
    vector<int> A;
    int b;
    cin>>a>>b;
    for(int i = a.size()-1;i>=0;i--)
    {
        A.push_back(a[i]-'0');
    }
    // for(int i =A.size()-1;i>=0;i--) printf("%d",A[i]);
    auto c = mul(A,b);
    for(int i =c.size()-1;i>=0;i--) 
    printf("%d",c[i]);
    return 0;
}

高精度除法

举例

image-20220730170549738

image-20220730170711931

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

输入格式
共两行,第一行包含整数 A,第二行包含整数 B。

输出格式
共两行,第一行输出所求的商,第二行输出所求余数。

数据范围
1≤A的长度≤100000,
1≤B≤10000,
B 一定不为 0
输入样例:
7
2
输出样例:
3
1
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;


//A/b 商是C,余数是r,r通过引用传递
vector<int> div(vector<int> &A,int b,int &r)
{
    vector<int> C;
    r = 0;  //r代表上一次的余数
    //除法从最高位开始看;注意i++还是i--
    for(int i = A.size()-1;i>=0;i--)
    {
        //r * 10 把r整体向高位移动一位,将最后一位空出来
        r = r * 10 + A[i];
        //从头开始除,r/b不会是两位数
        C.push_back(r / b);
        r %= b;
    }
    //C中最低位存的是最高位,最高位存的是最低位
    reverse(C.begin(),C.end());
    //前导0
    while(C.size() > 1 && C.back()==0) C.pop_back();
    return C;

}

int main()
{
    string a;
    vector<int> A;
    int b;
    cin>>a>>b;
    for(int i = a.size()-1;i>=0;i--)
    {
        A.push_back(a[i]-'0');
    }
    int r;
    auto c = div(A,b,r);
    for(int i =c.size()-1;i>=0;i--) 
    printf("%d",c[i]);
    cout<<endl<<r<<endl;
    return 0;
}

理解

代码背的不是字母,背的是思路


前缀和

一维前缀和

image-20220730173656402

image-20220730190218532

S0= 0

作用:快速求出原数组中一段的和

注意1:此处为Sr - Sl-1

image-20220730190328106

image-20220730190328150

image-20220730190357124

**注意2: 下标从1开始:**定义S0 = 0

好处:主要是处理边界,统一处理所有情况;不需要进行特判

image-20220730190512438

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

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

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

输入格式
第一行包含两个整数 n 和 m。

第二行包含 n 个整数,表示整数数列。

接下来 m 行,每行包含两个整数 l 和 r,表示一个询问的区间范围。

输出格式
共 m 行,每行输出一个询问的结果。

数据范围
1≤l≤r≤n,
1≤n,m≤100000,
−1000≤数列中元素的值≤1000
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10
#include <iostream>
using namespace std;
//统一表示数组长度,避免每次定义数组声明长度
const int N = 100010;
int n,m;
int a[N],s[N];
int main()
{
    scanf("%d%d",&n,&m);
    //对于数组输入来说,用for比用while更加方便
    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;
        scanf("%d%d",&l,&r);
        printf("%d\n",s[r]-s[l-1]);  //区间和的计算
    }
    return 0;
}

二维前缀和–求子矩阵中一部分和

求Sij–四部分矩形组合:拆出来aij==>Si-1,j Si,j-1 Si-1,j-1

image-20220731091125396

求子矩形的面积–四部分矩形组合

Sx2y2

X不变:X2,Y变:Y1-1

Y不变: Y2,X变:X1-1

Sx1-1,y2 Sx2,y1-1

Sx1-1,y1-1

image-20220730192505748

image-20220730192642667

具体分析

image-20220731095902001

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

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

输入格式
第一行包含三个整数 n,m,q。

接下来 n 行,每行包含 m 个整数,表示整数矩阵。

接下来 q 行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。

输出格式
共 q 行,每行输出一个询问的结果。

数据范围
1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4
输出样例:
17
27
21
#include <iostream>

const int N =1010;
int n,m,q;
int a[N][N],s[N][N];
int main()
{
    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++)
            s[i][j] = s[i-1][j] + s[i][j-1] - 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[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]);  //算子矩阵的部分和
        
    }
    return 0;
}

差分

一维差分

差分是前缀和的逆运算

image-20220731100459364

好处:对某个区间的数据加和减小了时间复杂度

image-20220731100731868

image-20220731100939744

image-20220731101041413

image-20220731101418307

差分:

a[N],b[N]全部初始化为0,b[N]不需要单独构造,只需要在a[N]基础上进行增值的操作即可,即不存在构造函数

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

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

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

输入格式
第一行包含两个整数 n 和 m。

第二行包含 n 个整数,表示整数序列。

接下来 m 行,每行包含三个整数 l,r,c,表示一个操作。

输出格式
共一行,包含 n 个整数,表示最终序列。

数据范围
1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000
输入样例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
输出样例:
3 4 5 3 4 2
#include <iostream>

using namespace std;

const int N = 100010;

int n,m;
int a[N],b[N];

void insert(int l,int r,int c)
{
    b[l] += c;
    b[r+1] -= c;
}

int main()
{
    scanf("%d%d",&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;    
}

二维差分

差分都不考虑人工构造,只考虑如何更新

image-20220731104324734

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

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

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

输入格式
第一行包含整数 n,m,q。

接下来 n 行,每行包含 m 个整数,表示整数矩阵。

接下来 q 行,每行包含 5 个整数 x1,y1,x2,y2,c,表示一个操作。

输出格式
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。

数据范围
1≤n,m≤1000,
1≤q≤100000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤c≤1000,
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
输出样例:
2 3 4 1
4 3 4 1
2 2 2 2
#include <iostream>

using namespace std;

const int N = 1010;
int a[N][N], b[N][N];
int n,m,q;

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()
{
    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-1][j]+b[i][j-1]-b[i-1][j-1];
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=m;j++)
            printf("%d ",b[i][j]);
        printf("\n");
    }

    return 0;
    
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值