算法笔记知识点整理大全

每次刷题都觉得自己吃了知识点不全,基础不牢固的亏,刷题的时候目标也不明确,于是看完了算法笔记并把知识点归纳了一下,当然直接看书会更加详细,这个归纳只是学习时加深印象以及方便自己之后回顾而已;之后刷题大概会根据这个大纲归纳一下具体题型,半个月之后看自己能刷多少吧^ _ ^

第二章 C/C++快速入门

2.1 基本数据类型及输入输出

2.1.2 变量类型及输入输出
类型 取值范围 占用字节 格式符
int 10的9次方以内整数 4字节(32位) %d
long long 10的10次方~10的18次方 8字节(64位) %lld
float 6~7位有效精度 32字节 %f
double 15~16位有效精度 64字节 %lf
char -128~127 -128~127 %c(字符串%s不加&)
bool 0/1 0/1
2.1.3 注意事项:
  1. 整型

long long赋值后要加LL;

整型加unsigned表示无符号,会把法术范围挪到正数上来;

  1. 浮点型

用double,放弃float;

  1. char型

ASCII码——09(4857)、AZ(6590)、az(97122) 小写字母比大写大32;

输出字符串格式scanf("%s",str);

2.1.4 三种实用输出格式
  1. %md

    不足m位的int变量右对齐输出,高位空格补齐;如果本身超过m位,则保持原样

  2. %0md

    不足m位的int变量右对齐输出,高位加0补齐;如果本身超过m位,则保持原样

  3. %.mf

    浮点数保留m位小数输出

2.1.5 getchar/putchar和typedef
  1. gerchar()——输入单个字符(一般用来识别换行符)
  2. putchar()——输出单个字符
  3. typedef long long LL;——起别名

2.2 常用math函数

f a b s ( d o u b l e x ) — — 取 绝 对 值 fabs(double x)——取绝对值 fabs(doublex)

p o w ( d o u b l e r , d o u b l e p ) — — r 的 p 次 方 pow(double r,double p)——r的p次方 pow(doublerdoublep)rp

f l o o r ( d o u b l e x ) / c e i l ( d o u b l e x ) — — 向 / 下 上 取 整 floor(double x) / ceil(double x)——向/下上取整 floor(doublex)/ceil(doublex)/

p o w ( d o u b l e r , d o u b l e p ) — — 返 回 r p pow(double r,double p)——返回r^p pow(doublerdoublep)rp

s q r t ( d o u b l e x ) — — 算 术 平 方 根 sqrt(double x)——算术平方根 sqrt(doublex)

l o g ( d o u b l e x ) — — 取 e 的 对 数 ( l o g a b = l o g e b / l o g e a ) log(double x)——取e的对数(log_ab=log_eb/log_ea) log(doublex)elogab=logeb/logea

s i n ( d o u b l e x ) / c o s ( d o u b l e x ) / t a n ( d o u b l e x ) / a s i n ( d o u b l e x ) / a c o s ( d o u b l e x ) / a t a n ( d o u b l e x ) 正 余 弦 sin(double x)/cos(double x) / tan(double x) /asin(double x) / acos(double x) /atan(double x) 正余弦 sin(doublex)/cos(doublex)/tan(doublex)/asin(doublex)/acos(doublex)/atan(doublex)

r o u n d ( d o u b l e x ) — — 四 舍 五 入 round(double x)——四舍五入 round(doublex)

2.3 结构体(struct)的使用

2.3.1 格式
struct 类型名{

//除自己外的所有数据类型

}结构体变量名;
2.3.2访问方法

“.”(普通变量)or"->’’(指针

2.3.3 初始化

结构体内部可以自定义初始化函数,示例

struct studenInfo{
int id;
char gender;
studenInfo(int _id,char _gender){
id=_id;
gender=_gender;
}
//可以简化为一行
//studenInfo(int _id,char _gender):id(_id),gender(_gender){}
}stu,*p;

2.4 memset赋值

对每个元素赋相同的值(0/-1)

格式:memset(a,-1,sizeof(a));

2.5 补充

2.5.1 浮点数的比较
const double eps=1e-8;
#define Equ(a,b) (fabs((a)-(b))<(eps))
2.5.2 各种复杂度
  1. 时间复杂度
    O ( 1 ) < O ( l o g n ) < O ( n ) < O ( n 2 ) O(1)<O(logn)<O(n)<O(n^2) O(1)<O(logn)<O(n)<O(n2)

    p s : 两 个 f o r 循 环 的 n 不 能 超 过 1000 , 因 为 空 间 复 杂 度 运 算 次 数 n 2 不 能 超 过 1 0 7 ps:两个for循环的n不能超过1000,因为空间复杂度运算次数n^2不能超过10^7 ps:forn1000n2107

    ps:两个for循环的n不能超过1000,因为空间复杂度不能超过

  2. 空间复杂度

    看数组大小或者其他数据结构大小

  3. 编码复杂度

    算法冗长,复杂度就大

第三章 入门:入门模拟

3.1 简单模拟

题目怎么说就·怎么做,不涉及算法

3.2 查找元素

给定元素,然后查找某个满足条件的元素。一般简单查找就是两个for遍历,查找算法第四章涉及

3.3 图形输出

  1. 找规律输出

  2. 定义二维数组输出

3.4 日期处理*

平年闰年/大月小月问题,需要细心

//判断闰年函数
bool isLeap(int year){
    return (year%4==0&&year%100!=0)||(year%400==0)//整除400或者整除4但不整除100
}

3.5 进制转换*

  1. P进制转换成十进制
//公式法
int y=0,pro=1;//y为最后十进制数,pro为每次的倍数
while(x!=0){
y=y+(x%10)*pro;//获得每次x的个位数
x=x/10;//去掉x的个位数
pro=pro*P;//倍数增长
}
  1. 十进制转换成Q进制
//除基取余法
int z[40],num=0;//z存放Q进制y的每一位,num为位数
do{
z[num++]=y%Q;
y=y/Q;
}while(y!=0)//商不为0时才循环,z数组从高位到低位为q进制数

3.6 字符串处理

3.6.1 相关函数

仔细分析输入输出格式,会有一些细节和边界情况,积累经验,熟练相关函数

gets ges(str) puts puts(str)
strcmp(str1,str2) 比较字符串大小(1<2(-)) strcpy(ste1,str2) 复制2—>1
strcat(str1,str2) str2拼接到str1后面 strlen(str) 字符个数
3.6.2 sscanf和sprintf
sscanf(str,"%d",&n);
sprintf(str,"%d",n);
//可以复杂一点,格式和scanf/printf差不多

第四章 入门:算法初步

4.1 排序

4.1.1 选择排序
//i从[0,n-1]枚举,待排序部分[i,n-1],从小到大,选出最小
int selectSort(){
    for(int i=0,i<n,i++){
        int k=i;
        for(int j=i,j<n,j++){
            if(a[j]<a[k])
                k=j;
        }
        int temp=a[i];//交换位置
        a[i]=a[k];
        a[k]=temp;
    }
    return 0;
}
4.1.2 冒泡排序
//相邻元素比较,从小到大,有序输出
int bubbleSort(){
    for (int i=0; i<n-1; i++){
         for (int j = 0; j < n - 1 - i; j++){
                        if (a[j] > a[j + 1]){
                            int temp=a[j];//交换位置
                            a[j]=a[j+1];
                            a[j+1]=temp;
                        }//左边数更大就换  
                } 
    }
     return 0;              
}
4.1.3 插入排序
//将数组无序部分插入已有序部分
int insertSort(){
 for(int i=1;i<len;i++){
                int key=a[i];
                int j=i;
                while((j>=0) && (key<a[j-1])){
                        a[j]=a[j-1];//数组后移
                        j--;
                }
                a[j]=key;//插入
        }
     return 0;               
}
4.1.4 排序题和sort函数的应用
#include<algorithm>
编写bool cmp()函数
sort(首地址,尾地址的下一个,cmp)

4.2散列

4.2.1散列(hash)定义和整数散列

将元素通过一个函数转换为整数,时该整数可以尽量唯一的代表这个元素

const int maxn=10010;
bool hashTable[maxn]={false};

key是整数:

1.直接定址法 2.平方取中法 3. 除留余数法

冲突三种方法:

1.线性探查法 2.平方探查法 3.链地址法

4.2.2字符串hash初步

举例:将二维坐标P映射成整数H§=x*Range+y;

字符串hash是指将字符串S映射成整数 AZ=025,az=2651,整数52~62或者直接拼接

int hashFunc(char S[],int len){
    int id=0;
    for(int i=0;i<len;i++){
        if(S[i]<='A'&&S[i]>='Z'){
            id=id*52+(S[i]-'A');
        }else if(S[i]<='a'&&S[i]>='z'){
            id=id*52+(S[i]-'a')+26;
        }
    }
    return 0;
}

4.3递归

4.3.1 分治

分解--------解决---------合并

4.3.2 递归
  1. 递归边界:分解的尽头

  2. 递归式:将原问题分解成子问题的手段

  3. 相关问题:---------全排列------n皇后问题

4.4贪心

--------考虑当前状态下局部最优

总的来说,贪心是用来解决一类最优化问题,并希望由局部最优解来推得全局最优解的算法思想.贪心算法适用的问题一定满足最优子结构性质,即一个问题的最优解可以由子问题的最优解有效构造出来.

4.5二分

4.5.1二分查找
#include<stdio.h>
//a[]严格递增,一般采用非递归
int binarySearch(int a[],int left,int right,int x){
    int mid;
    while(left<=right){
        mid=(left+right)/2;
        if(a[mid]==x) return mid;
        else if(a[mid]>x){
            right=mid-1;
        }else{
            left=mid+1;
        }
    }
    return -1;//查找失败
}
int main(){
    const int n=10;
    int a[n]={1,2,3,4,5,6,7,8,9,10};
    printf("%d",binarySearch(a,0,n-1,6));
    return 0;
}

ps:如果二分上界超过int数据一半,可能溢出,此时用mid=left+(right-left)/2代替mid=(left+right)/2

4.5.2二分法拓展

求近似平方根问题/装水问题/木棒切割问题

4.5.3快速幂

快速幂又叫二分幂,基于以下事实:
1. 如 果 b 是 偶 数 , 那 么 a b = a ∗ a b − 1 1.如果b是偶数,那么a^b=a*a^{b-1} 1.b,ab=aab1

2. 如 果 b 是 奇 数 , 那 么 a b = a b / 2 ∗ a b / 2 2.如果b是奇数,那么a^b=a^{b/2}*a^{b/2} 2.b,ab=ab/2ab/2

  1. 快速幂的递归写法,时间复杂度O(logb)
typedef long long LL;
//求a^b%m
LL binaryPow(LL a,LL b,LL m){
    if(b==0) 
        return 1;
    else if(b%2==1) //可以用if(b&1)代替
        return a*binaryPow(a,b-1,m)%m;
    else{
        int mul=binaryPow(a,b/2,m);
        return mul*mul%m;
    }
}
  1. 快速幂的迭代写法(效率差异不高):
typedef long long LL;
//求a^b%m
LL binaryPow(LL a,LL b,LL m){
    LL ans=1;
    while(b>0){
        if(b&1){
            ans=ans*a%m;
        }
        a=a*a%m;
        b>>=1;
    }
    return ans;
}

4.6two points

4.6.1 概念

利用问题本身和序列特性,使用两个下标i,j对序列进行扫描(同向或反向),从而以较低复杂度(一般是O(n))解决问题

//a[n]递增序列,正整数M,求a[i]+a[j]=M,two points思想示例
while(i<j){
    if(a[i]+a[j]==m){
        ptintf("%d %d\n",i,j);
        i++;
        j--;
    }else if(a[i]+a[j]<m){
        i++;
    }else{
        j--;
    }
}//序列合并问题在下一小节有递归和非递归实现
4.6.2归并排序

2-路归并排序思想:------将序列不断两两分组,组内单独排序,然后不断合并.

const int maxn=100;
//将数组的[L1,R1]与[L2,R2]区间合井为有序区间(此处L2即为R1 +1)
void merge(int A[],int L1,int R1,int L2,int R2){
    int i=L1,j=L2;//i指向A[L1],j指向A[L2]
    int temp[maxn],index=0;//temp临时存放合并后的数组,index为下标    
    while(i <= R1&&j<=R2){
       if(A[i] <= A[j]) {
           temp[index++] = A[i++];//将A[i]加入序列temp
       }else{
           temp[index++] = A[j++];//将A[j]加入序列temp
       }
    }
    while(i <= R1) temp[index++] = A[i++]; //将[L1, R1]的剩余元素加人
    while(j <= R2) temp[index++] = A[j++]; //将[L2, R2]的剩余元素加入
    for(i = 0; i < index; i++) {
        A[L1+i]=temp[i];//将合并后的序列赋值回数组A
    }
}
void mergeSort(int A[],int left,int right){
    if(left<right){
        int mid=(left+right)/2;//取中点
        mergeSort(A,left,mid);//递归
        mergeSort(A,mid+1,right);//递归
        merge(A,left,mid,mid+1,right);//合并
    }
}
4.6.3快速排序

把A[1]存入temp,让A[1]左边数都比他小,右边数都比他大的问题:

//递归实现
int Partition(int A[],int left,int right){
    int temp=A[left];
    while(left<right){
        while(left<right&&A[right]>temp) right--;
        A[left]=A[right];
        
        while(left<right&&A[left]<=temp) left++;
        A[right]=A[left];
    }
    A[left]=temp;
    return left;
}
void quickSort(int A[],int left,int right){
    if(left<right){
        int pos=Partition(A,left,right);
        quickSort(A,left,pos-1);
        quickSort(A,pos+1,right);//反复递归排序直到全部有序
    }
}
4.6.4生成随机数
#include<stdio.h>//程序必备
#include<stdlib.h>//随机数必备
#include<time.h>//随机数必备
int main(){
    srand((unsigned)time(null));//main方法第一句,生成随机数种子
    for(int i=0;i<10;i++){
        printf("%d",rand());
    }
    return 0;
}

注意:

  1. 生成的是[0,RAND_MAX]范围内整数,如果想输出[a,b]范围内,需要使用rand()%(b-a+1)+a;显然rand()%(b-a+1)的范围是[0,b-a],再加上a就是[a,b]

  2. 想生成更大范围的随机数

    (int)(round(1.0rand()/RAND_MAX(b-a)+a)

4.7其他高效技巧与算法

4.7.1打表
  1. 在程序中一次性计算出所有需要用到的结果,之后的查询直接取这些结果;
  2. 在程序B中分一次或多次计算出所有需要用到的结果,写在程序A中结果集中,然后在程序A中就可以直接使用这些结果;
  3. 先暴力计算小范围数据,再找规律.
4.7.2 活用递推

一种思想,细心考虑找出题目中递推关系.

4.7.3随机选择算法

原理:类似于随即快速排序算法,

问题:从一个无序数组中求得第K大的数字

//递归实现
int randPartition(int A[],int left,int right){
    int temp=A[left];
    while(left<right){
        while(left<right&&A[right]>temp) right--;
        A[left]=A[right];
        
        while(left<right&&A[left]<=temp) left++;
        A[right]=A[left];
    }
    A[left]=temp;
    return left;
}
int randSelect(int a[],int left,int right,int K){
    if(left==right) return a[left];
    int p=randPartition(a,left,right);
    int M=p-left+1;//主元是a[p],第p-left+1大的数
    if(K==M) return a[p]
    else if(K<M){
        randSelect(a,left,p-1,K);//往左找
    }else{
        randSelect(a,p+1,right,K-M);//往右找
    }
}

第五章 入门:数学问题

5.1简单数学

掌握简单的数理逻辑,就是些水题,ccf第一题那种.

5.2最大公约数与最小公倍数

5.2.1最大公约数

--------------------实现原理:欧几里得算法(辗转相除法)

  1. 递归式: gcd(a,b)=gcd(b,a%b)
  2. 递归边界: gcd(a,0)=a
  3. 代码实现:
int gcd(int a,int b){
        if(b==0) return a;
        else return gcd(b,a%b);
}
//更简洁的写法
int gcd(int a,int b){
    return !b?a:gcd(b,a%b);
}
5.2.2最小公倍数
  1. 公式:a*b/d
  2. 代码实现
int main(){
    int d=gcd(a,b);
    printf("%d",(a*b/d));
}

5.3分数的四则运算

所谓的分数的四则运算是指,给定两个分数的分子和分母,求他们加减乘除的结果

5.3.1分数的表示和化简.
  1. 分数的表示-------对个分数来说, 最简洁的写法就是写成假分数的形式, 即无论分子比分母大或者小,都保留其原数。因此可以使用一个结构体来存储这种只有分子和分母的分数:

    于是就可以定义Fraction 类型的变量来表示分数,或者定义数组来表示一堆分数。其中需要对这种表示制订三项规则:
    ①使down为非负数。如果分数为负,那么令分子up为负即可。
    ②如果该分数恰为0,那么规定其分子为0,分母为1。
    ③分子和分母没有除了1以外的公约数。

    struct Fraction{//分数
    int up, down;//分子、分母
    }
    
  2. 分数的化简-------分数的化简主要用来使Fraction变量满足分数表示的三项规定,因此化简步骤也分为以下三步:
    ①如果分母down为负数,那么令分子up和分母down都变为相反数。
    ②如果分子up为0,那么令分母down为1.
    ③约分:求出分子绝对值与分母绝对值的最大公约数d,然后令分子分母同时除以d.
    代码如下:

    Fraction reduction (Fraction result){
    if(result.down < 0) {
    //分母为负数,令分子和分母都变为相反数
    result.up = -result.up;
    result.down = - result.down;
    }
    //如果分子为0,令分母为1
    if (result.up == 0) {
    result.down = 1;
    }
    //如果分子不为0,进行约分
    else {
    int d=gcd(abs (result .up), abs (result dow)); //分子分母的最大公约数
    result.up /= d;//约去最大公约数
    result.down /= d;
    return result; 
    }
    
5.3.2分数的四则运算
  1. 加减:

result=(f1.up*f2.down+f1.down*f2.up)/(f1.down*f2.down)

result=(f1.up*f2.down-f1.down*f2.up)/(f1.down*f2.down)

  1. 乘除

result=(f1.up*f2.up)/(f1.down*f2.down)

result=(f1.up*f2.down)/(f1.down*f2.up)

  1. ps:必须当心判断除数不为0
5.3.3分数的输出
  1. 分数的输出根据题目的要求进行,但是大体上有以下几个注意点:
    ①输出分数前,先化简
    ②如果分数r的分母down为1,该分数是整数,一般来说题目会要求直接输出分子而省略分母。
    ③如果分数r的分子up的绝对值>分母down (想一想分子为什么要取绝对值? ),说明该分数是假分数,此时应按带分数的形式输出,即整数部分为r.up /r.down,分子部分为
    abs(r.up) % r.down,分母部分为r.down.
    ④以上均不满足时说明分数r是真分数,按原样输出即可。

  2. 以下是一个输出示例:

    void showResult (Fraction r) {
    //输出分数,先分数化简
    r=reduction(r) ;
    //整数
    if(r.down==1) printf("%lld", r.up) ;
    //假分数
    else if(abs(r.up) > r.down) {
    printf("%d %d/%d", r.up / r.down, abs(r.up) %r.down, r.down) ;
    } else {
    //真分数
    printf ("%d%d", r.up,r.down) ;
    /*强调一点:由于分数的乘法和除法的过程中可能使分子或分母超过int型表示范围,因
    此一般情况下,分子和分母应当使用long long型来存储。*/
    

5.4素数

1不是素数,也不是合数

5.4.1素数判断

如果存在被整除的数,一定有一个小于sqrt(n),一个大于sqrt(n)

bool isPrime(int n){
    if(n==1) return false;
    int sqr=(int)sqrt(n*1.0);
    for(int i=2;i<=sqr;i++){
        if(n%i==0) return false;
    }
    return ture;
}
5.4.2素数表
//n为10的5次方以内的范围
const int maxn=101;
int Prime[maxn],pNum=0;
bool p[maxn]={0};
int Find_Prime(){
    for(int i=1;i<maxn;i++){
        if(isPrime[i]){
            Prime[pNum++]=i;
            p[i]=1;
        }
    }
}
5.4.3埃氏筛法:

------------如果要求更大的数,则启用

//n为10的5次方以内的范围
const int maxn=101;
int Prime[maxn],pNum=0;
bool p[maxn]={0};
int Find_Prime(){
    for(int i=2;i<maxn;i++){
        if(p[i]==false){
            Prime[pNum++]=i;
            for(int j=i+i;j<maxn;j+=i){
                //筛去所有i的倍数,循环条件不能写成j<=maxn
                p[j]=1;
            }
        }
    }
}

5.5 质因子分解

------------将一个正整数分解成一个或多个质数的乘积的形式,分解步骤:

  1. 创建一个结构体:

    struct factor{
        int x,cnt;//x_质因子,cnt_其个数
    }fac[10];//fac数组开到10就可以了,不论题目
    
  2. 枚举1~sqrt(n)所有质因子p,判断是否是n的质因子

    int num=0;
    if(n%prime[i]==0){//如果是质因子
        fac[num].x=prime[i];//增加质因子
        fac[num].cnt=0;//初始化个数
        while(n%prime[i]==0){
            fac[num].cnt++;//计算个数
            n/=prime[i];
        }
        num++;//检查下一个质因子
    }
    
  3. 上述步骤结束后仍然大于1,还有个大于sqrt(n)的质因子n

    if(n!=1){//无法被除尽
        fac[num].x=n;//把n放进去
        fac[num++].cnt=1;
    }
    

5.6大整数运算

-----------定义:高精度的整数,就是无法用基础数据类型存储其精度的整数

5.6.1大整数的存储
  1. 定义一个整数数组存储,高位存高位,低位存低位的顺序存储,为了方便获取长度,一般定义一个结构体:
struct bign{
    int len;
    int d[1000];
    bign(){
        memset(d,0,sizeof(d));
        int len=0;
    }//每次结构体变量被定义都会自动初始化
}
  1. 但每次输入大整数一般用字符串先读入,然后再把字符串另存为bign结构体。由于char数组读入会翻转,高位存低位,低位存高位的顺序存储,即我们需要再翻转回来
bign change(char str[]){
    bign a;
    a.len=strlen(str);
    for(int i=0;i<a.len;i++){
        a.d[i]=str[a.len-i-1]-'0';//逆着赋值
    }
    return 0;
}
  1. 如果要比较两个大整数大小的规则:1.判断len大小;2.从高到低比较数字大小。
5.6.2大整数的四则运算

类似于列竖式:

//高精度加法
bign add(bign a,bign b){
    bign c;
    int carry=0;
    for(int i=0;i<a.len||i<b.len;i++){
        int temp=a.d[i]+b.d[i]+carry;
        c.d[c.len++]=temp%10;//个位为结果
        carry=temp/10;//10位为进位
    }if(carry!=0){
        c.d[c.len++]=carry;
    }
    return c
}
//注意point:这个模板针对非负整数,如果有单个负数,可以转换时去负号,用减法
//如果都是负数,取绝对值用加法,然后结果加上负数
//高精度减法
bign sub(bign a,bign b){
    bign c;
    int carry=0;
    for(int i=0;i<a.len||i<b.len;i++){
        if(a.d[i]<b.d[i]){//不够减
            a.d[i+1]--;//向高位借位
            a.d[i]+=10;//加10
        }
        c.d[c.len++]=a.d[i]-b.d[i];//减法结果为当前位结果
        while(c.len-1>=1&&c.d[c.len-1]==0){//去除最高位的0且至少保留一位最低位
            c.len--;
        }
    }
    return c
}
//注意point:使用sub前比较两个数的大小,如果被减数小于减数,则调换数组相减,结果加上负号
//高精度与低精度乘法
bign multi(bign a,int b){
   
    bign c;
    int carry=0;
    for(int i=0;i<a.len;i++){
   
        int temp=a.d[i]*b+carry;
        c.d[c.len++]==temp%10;
        carry=temp/10;
    }whiel(carry!=0){
   
        c.d[c.len++]=carry%10;
        carry=carry/10;
    }
    return c
}
高精度与低精度除法
bign multi(bign a,int b,int &r){
   //r为余数
    bign c;
    c.len=a.len;//被除数与商位数一一对应,长度相等
    for(int i=a.len-1;i>=0;i--){
   
        r=r*10+a.d[i];
        if(r<b) c.d[i]=0//不够除
        else{
   //够除
             c.d[i]=r/b;
             r=r%b;
            }
    }
    while(c.len-1>=1&&c.d[c.len-1]==0){
   //去除最高位的0且至少保留一位最低位
        c.len--;
    }
    return c
}

5.7扩展欧几里得算法

暂时不学,有需要再回来

5.8组合数

5.8.1关于n!的一个问题

n ! 中 有 ( n / p + n / p 2 + n / p 3 + . . . ) 个 质 因 子 n!中有(n/p+n/p^2+n/p^3+...)个质因子 n!n/p+n/p2+n/p3+...)

//n!中有多少个质因子p
int cal(int n,int p){
   
    int ans=0;
    while(n){
   
        ans+=n/p;
        n/=p;  
    }
    return ans;
}

上面这个可以推广计算n!的末尾有多少个0(等于n!中因子10的个数)

一个概念:

n!中质因子p的个数,实际上等于1~n中p的倍数的个数n/p加上n/p!中质因子p的个数

//因此得出cal的递归版本
int cal(int n,int p){
   
   if(n<p) return 0;//n<p时,不可能再存在质因子
    else return n/p+cal(n/p,p)//返回n/p!中质因子p的个数**
}
5.8.2组合数的计算

方法一:根据定义式计算------暴力破解,容易溢出,忽略

方法二:根据递推式计算
递 归 公 式 : C n m = C n − 1 m + C n − 1 m − 1 递归公式:C_n^m=C_{n-1}^m+C_{n-1}^{m-1} Cnm=Cn1m+Cn1m1

递 归 边 界 : C n 0 = C n n = 1 递归边界:C_n^0=C_n^n=1 Cn0=Cnn=

  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值