欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

算法笔记 - 数学问题

程序员文章站 2022-07-12 22:20:07
...

第七章 高精度计算

7.1 大数加法

//输入cin>> char a[MAX] char b[MAX] 定义 result[MAX*2+10]
void add(char *a,char *b)
{
    //定义 int []分别对应三组
    int aa[MAX],bb[MAX],temp[MAX*2+10];
    memset(aa,0,sizeof(aa));//因为是函数里申请的数组所以如果不memset会随机赋初值!
    memset(bb,0,sizeof(bb));
    memset(temp,0,sizeof(temp));
    int lena = strlen(a),lenb = strlen(b);
    //转换
    for(int i=0;i<lena;i++)
        aa[i]=a[lena-1-i]-'0';
    for(int j=0;j<lenb;j++)
        bb[j]=b[lenb-1-j]-'0';
    //加
    for(int i=0;i<max(lena,lenb);i++)
            temp[i]+=(aa[i]+bb[i]);
    //进位
    int jinwei =0;
    for(int i=0;i<max(lena,lenb);i++)
    {
        int mid = temp[i]+jinwei;
        temp[i]=mid%10;
        jinwei = mid /10;
    }
    //前导零
    int index = max(lena,lenb)-1;
    while(temp[index]==0) index--;
    //转换
    if(index <0 ) result[0]='0'; //index<0说明结果是0,输出零
    else
        for(int i=0;i<=index;i++)
            result[i]=temp[index-i]+'0';
}
//输出cout<< char result[]

7.2 大数乘法

//输入cin>> char a[MAX] char b[MAX] 定义 result[MAX*2+10]
void mul(char *a,char *b)
{
    //定义 int []分别对应三组
    int aa[MAX],bb[MAX],temp[MAX*2+10];
    memset(aa,0,sizeof(aa));//因为是函数里申请的数组所以如果不memset会随机赋初值!
    memset(bb,0,sizeof(bb));
    memset(temp,0,sizeof(temp));
    int lena = strlen(a),lenb = strlen(b);
    //转换
    for(int i=0;i<lena;i++)
        aa[i]=a[lena-1-i]-'0';
    for(int j=0;j<lenb;j++)
        bb[j]=b[lenb-1-j]-'0';
    //加 !!
    for(int i=0;i<lena;i++)   // 与加法不同这里是二重循环,乘数的每一位都会和被乘数的每一位乘一次,相当于每一位要经过lenb次大数加法
        for(int j=0;j<lenb;j++)
            temp[i+j]+=(aa[i]*bb[j]);
    //进位 !!
    int jinwei =0;
    for(int i=0;i<lena+lenb;i++)//i的范围也和加法不一样,因为a*b的结果最多可能会有lena+lenb位
    {
        int mid = temp[i]+jinwei;
        temp[i]=mid%10;
        jinwei = mid /10;
    }
    //前导零
    int index = lena+lenb-1;//i的范围也和加法不一样,因为a*b的结果最多可能会有lena+lenb位
    while(temp[index]==0) index--;
    //转换
    if(index <0 ) result[0]='0'; //index<0说明结果是0,输出零
    else
        for(int i=0;i<=index;i++)
            result[i]=temp[index-i]+'0';
}
//输出cout<< char result[]

7.3 大数减法(类似大数加法)

例题

2809 计算 2的n次方 大整数和普通整数的乘法(只要一重循环)
算法笔记 - 数学问题

#include<cstdio>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#define MAX 103
#define  num 32767
#define INF 0x7f7f7f
using namespace std;
int n;
int main()
{
   //freopen("input.txt","r",stdin);
   cin>>n;
   int s[MAX]={1},jinwei=0; 
   //加
   for(int times=0;times<n;times++)
   {
       for(int j=0;j<MAX;j++)//大整数和小整数的乘法规则是大整数的每一位依次与小整数整体相乘
       {
           int temp =s[j]*2+jinwei;
           s[j]=temp%10;
           jinwei = temp/10;
       }
   }
   //前导0
   int k = MAX-1;
   while(s[k]==0) k--;
   for( int i =k;i>=0;i--) cout<<s[i];
   return 0;
}

2738 实数加法
算法笔记 - 数学问题

#include<cstdio>
#include<cstring>
const int LEN = 100;
char num1[LEN + 10],num2[LEN + 10];
int aint[LEN],aflo[LEN],int1[LEN],int2[LEN],flo1[LEN],flo2[LEN];
int main() {
    scanf("%s",num1);
    scanf("%s",num2);
    int len1 = strlen(num1),len2 = strlen(num2),pos1,pos2,id1 = 0,id2 = 0;
    //利用strchr(数组,字符)-数组 得到小数点下标
    pos1 = strchr(num1,'.') - num1, pos2 = strchr(num2,'.') - num2;
    //对小数点后面的数转换
    for(int i = pos1 + 1; i < len1; i++){
        flo1[id1++] = num1[i] - '0';
    }
    for(int i = pos2 + 1; i < len2; i++){
        flo2[id2++] = num2[i] - '0';
    }
    id1 = id1 > id2 ? id1 - 1 : id2 - 1;//就是求max(lena,lenb)
    //对小数点后面得数加 和进位
    for(int i = id1; i > 0; i--){
        aflo[i] += flo1[i] + flo2[i];
        if(aflo[i] >= 10){
            aflo[i] -= 10;
            aflo[i - 1]++;
        }
    }
    //注意衔接的地方小数部分到整数部分的进位
    aflo[0] += flo1[0] + flo2[0];
    if(aflo[0] >= 10){
        aflo[0] -= 10;
        aint[0] ++;
    }
    //对小数点前面的数转换
    id1 = id2 = 0;
    for(int i = pos1 - 1; i >= 0; i--){
        int1[id1++] = num1[i] - '0';
    }
    for(int i = pos2 - 1; i >= 0; i--){
        int2[id2++] = num2[i] - '0';
    }
    //对小数点前面的数加
    id1 = id1 > id2 ? id1 : id2;
    for(int i = 0; i < id1; i++){
        aint[i] += int1[i] + int2[i];
        if(aint[i] >= 10){
            aint[i]-= 10;
            aint[i+1]++;
        }
    }
    //输出小数点前的数
    //前导零
    for(id2 = id1; aint[id2] == 0 && id2 >= 0;id2--);
    for(int i = id2; i >= 0; i--) printf("%d",aint[i]);
    if(id2 < 0) putchar('0');
    putchar('.');
    //输出小数点后面的数
    //前导零
    for(id1 = LEN - 1; aflo[id1] == 0 && id1 >= 0;id1--);
    for(int i = 0; i <= id1; i++) printf("%d",aflo[i]);
    putchar('\n');
    return 0;  
}

第七.1章 素数和质因子分解

1 素数判断

 bool isPrime(int n)
 {
     if(n<=1) return 0; //注意1也是素数
    int sqr = (int) sqrt(1.0*n);
    for(int i=2;i<=sqr;i++)//从2开始,遍历到根号下n
        if(n%i==0) return 0;
    return 1;
 }

2 素数表获取(艾氏筛选)

bool heshu[MAX];//一个标记数组记录和数
int zhishu[MAX], index=0;//一个int 数组记录质数(也就是素数)
for(int i=2;i<MAX;i++)//从2开始遍历
{
    if(!heshu[i]) //如果不是和数那就是质数
    {
        zhishu[index++]=i;
        for(int j=i;j<MAX;j=j+i) heshu[j]=true;
    }
}

3 质因子分解(180 = 2^2 * 3^2 * 5^1)

//定义结构体保存因子和他的指数
struct factor
{
    int yinzi;
    int zhishu;
}fac[10];
//先求2-sqrt(n)的素数表(艾氏筛选)  int zhishu[MAX],int index
;
//遍历素数表
for(int i=0;i<index;i++)
{
    if(n%zhishu[i]==0)
    {
        //存入结构体数组
        fac[cnt].yinzi = zhishu[i];
        fac[cnt].zhishu=0;
        //使因子的指数最大
        while(n%zhishu[i]==0)
        {
            fac[cnt].zhishu++;
            n/=zhishu[i];
        }
        cnt++;
    }
}
//找比sqrtn大的质因子(如果有的话只能是n本身)
if(n>1)
{
    fac[num].yinzi=n;
    fac[num++].zhishu=1;
}

第七.2章 最大公约数(gcd)和最小公倍数以及分数运算

1 最大公约数

//保证a>b
int gcd(int a ,int b)
{
    if(b==0) return a;
    return gcd(b,a%b);
} 

2 最小公倍数

//先求最大公约数
int zuidagongyueshu = gcd(a,b);
//再求最小公倍数
int zuixiaogongbeishu = a/zuidagongyueshu*b;

3 分数的化简

//定义结构体表示分数
struct fenshu
{
    int fenzi,fenmu;
};
//化简
fenshu  huajian(fenshu  &a)
{
    //原则1 分母为非负数
    if(a.fenmu<0)
    {
        a.fenzi=-a.fenzi;
        a.fenmu = -a.fenmu;
    }
    //原则2 分子为0则分母为1
    if(a.fenzi==0)
        a.fenmu==1;
    //原则3 分子不为0则进行约分
    if(a.fenzi!=0)
    {
        int big = abs(max(a.fenzi,a.fenmu));//注意用绝对值计算gcd
        int small =abs(min(a.fenzi,a.fenmu));
        int temp = gcd(big,small);
        a.fenzi/=temp;
        a.fenmu/=temp;
    }
    return a;
}

第三章 数制转换

3.1 其他进制转换十进制

//读入 char a[MAX] 类似大数
int jishu =1;
for(int i=lena-1;i>=0;i--)//类似大数,从读入的最后一位开始处理
{
    result += (a[i]-'0')*jishu;
    jishu*=jinzhi;
}

3.2 十进制转换其他进制

//读入 int a 定义char  result[MAX]; int index
do
{
    result[index++]=a%jinzhi+'0';
    a=a/jinzhi;
}while(a);//使用do while 为了避免小于jiznhi的数无法存储

第五章 日期处理
5.1 知两个日期求天数

 //定义 int month[2][13];  int years[2];  bool isRunNian();
 //年
 if(year2>year1+1) 
    for(int i=year1+1;i<year2;i++) day+=years[isRunnian(i)];
 //月日
 if(year2==year1+1)
 {
     //先求和后去首尾
     for(int i= m1;i<=12;i++) day+=month[isRunnian(year1)][i];
     for(int i=1;i<=m2;i++) day+=month[isRunnian(year2)][i];
     day-=d1;
     day-=(month[isRunnian(year2)][m2]-d2);
 }
 else
 {
     //先求和后去首尾
     for(int i=m1;i<=m2;i++) day+=month[isRunnian(year1)][i];
     day-=d1;
     day-=(month[isRunnian(year2)][m2]-d2);
 }

5.2知道天数和开始日期求结束日期

//输入天数day ,开始日期 year1 m1 d1
while(day--)
{
    //日
    d1++;
    //月
    if(d1==month[isRunnian(year1)][m1]+1)
    {
        d1=1;
        m1++;
    }
    //年
    if(m1==13)
    {
        m1=1;
        year1++;
    }
}
//d2=d1.m2=m1,year2=year1

第三.1章 二分

1 对已经排好序的数组二分

#include <algorithm>
int a[MAX];//对已经排好序的数组直接二分可以使用stl
//升序数组返回第一个大于等于num的指针
lower_bound(a,a+MAX,num);
//升序数组返回第一个大于等于num的下标
lower_bound(a,a+MAX,num)-a;
//升序数组返回第一个大于num的指针
upper_bound(a,a+MAX,num);
//升序数组返回第一个大于num的下标
upper_bound(a,a+MAX,num)-a;
//升序数组返回num的个数
upper_bound(a,a+MAX,num)-lower_bound(a,a+MAX,num);

//降序数组返回第一个小于等于num的指针
lower_bound(a,a+MAX,num,greater<int>() );

2 对浮点数的枚举通常采用二分(实数二分)

//要求解空间和要求的结果有线性关系(单增单减)
//定义精度 #define eps 1e-5
double solve(double L,double R)
{
    double left = L,right = R,mid;
    while(right-left>eps)//注意精度
    {
        mid = left+(right-left)/2.0;
        if(结果大了) right =mid;//右不缩进
        else left = mid;//做不缩进
    }
    return mid;
}

3 整数二分(<,<= 右缩进 >,>= 左缩进 == 左右都缩进)

//1 二分查找区间内某个数字的下标(存在且唯一),不存在返回-1:
int search(int l,int r,int x)
{
    int mid;
    while (l<=r)//唯一一个需要带=的二分
    {
        mid=(l+r)>>1;
        if(a[mid]==x) return mid;//三分支存在且唯一
        if(a[mid]<x) l=mid+1;//左缩进
        else r=mid-1;//右缩进
    }
    return -1;
}

//2 查询区间内<=x的最大值(有多个最大值时返回最靠右的坐标):
int search(int l,int r,int x)
{
    int mid;
    while (l<r)
    {
        mid=(l+r+1)>>1;
        if(a[mid]<=x) l=mid; //左不缩进,如果查询<x最大值直接改成<即可
        else r=mid-1;//(因为是返回靠右的坐标)右缩进
    }
    return l;
}

//3 查询区间内>=x的最小值(有多个最小值时返回最靠左的坐标)
int search(int l,int r,int x)
{
    int mid;
    while (l<r)
    {
        mid=(l+r)>>1;
        if(a[mid]>=x) r=mid;//右不缩进
        else l=mid+1;//(因为是返回靠左的坐标)左缩进
    }
    return l;
}

例题

木桶装水问题
2774 木材加工
算法笔记 - 数学问题

#include<cstdio>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#define MAX 10003
#define  num 32767
#define INF 0x7f7f7f
#define eps 1e-5
using namespace std;
int N,K,l,r,m;
int ym[MAX];
//长度和段数有明显的线性关系,用二分
int main()
{
    //freopen("input.txt","r",stdin);
    cin>>N>>K;
    long long int sum=0;
    for(int i=0;i<N;i++)
    {
        cin>>ym[i];
        sum+=ym[i];
    }
    //注意特殊情况
    if(sum<K*1)
    {
        cout<<0<<endl;
        return 0;
    }
    if(sum==K*1)
    {
        cout<<1<<endl;
        return 0;
    }
    l=1;r=sum/K; //代表能切出k个的子原木的长度
    //二分
    int maxlen=0;
    while(l<r)//这里是对整数二分素以不用eps
    {
        m=(r-l)/2+l;
        int cnt=0;
        //计算能分成长m的子段个数
        for(int i=0;i<N;i++)
            cnt+=ym[i]/m;
        if(cnt>=K){
            if(maxlen<m) maxlen=m;
            l=m+1;//这里如果l=m就会TLE,因为前面用了>=所以这里用左缩进
         }
        else  r=m;
    }
    cout<<maxlen;
   return 0;
}

第四章 排序
4.1 sort 快排

#include<algorithm>
bool cmp();
sort(首地址,尾地址,cmp);
stable_sort(首地址,尾地址,cmp);//值相同的情况下保证是稳定排序
//结构体类型的排序
vector<结构体> vt;
sort(vt.begin(),vt.end(),cmp);
//普通类型排序
int/char/double a[MAX];
sort(a,a+MAX,cmp);

4.2 priority_queue堆排序

#include<queue>
//结构体类型的排序
struct cmp
{
    bool operator() (结构体 j1,结构体 j2)
    {return j1.num>j2.num?j1.num:j2.num;}
};
priority_queue<结构体,vector<结构体>, cmp >;
//普通类型的堆排序
priority_queue<int/char/double, vector<int/char/double>, greater<int/char/double> >;
priority_queue<int/char/double, vector<int/char/double>, less<int/char/double> >;