四个基本的计数原理

《Introductory Combinatorics Fifth Edition》学习笔记:

四个基本计数原理:

@加法原理:设集合s划分为两两互不相交的各个部分s1,s2,s3……sn,那么s的元素总个数等于各个部分的总个数之和。即|s|=|s1|+|s2|+……+|sn|.(如果允许子部分相交,则需要用容斥原理来解决)使用加法原理的技巧是:把原集合分割成容易处理的少量子集。
例子:一名学生想要选修一门数学课程和一门计算机的课程,数学课程有三门可供他选择,计算机课程有四门可以选择,那么该学生有多少种选择?
s=s1+s2=3+4=7
@乘法原理:设s是有序对(a,b)的集合,a来自大小为p的集合s1,对于每一个a都有q个不同的b元素和其对应。于是|s|=p*q
例子:确定一个正整数的正因子的总个数?

分析:一个正整数可以进行唯一的素因子分解,比如12=2^2*3,那么12的正因子为2的指数总个数和3的指数总个数之积;3*2=6.

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const int maxn=1e8+1,maxn2=1e7;
int pri[maxn2],top=0,pow[maxn2];
bool notpri[maxn];
void getpri(){
    for(int i=2;i<maxn;i++){
        if(!notpri[i])pri[top++]=i;
        for(int j=0;j<top&&pri[j]*i<maxn;j++){
             notpri[pri[j]*i]=1;
             if(i%pri[j]==0)break;
        }
    }
}
int main()
{
    //freopen("cin.txt","r",stdin);
    LL n,top2=0,i;
    getpri();
    while(cin>>n){
        top2=0;
        memset(pow,0,sizeof(pow));
        for(i=0;i<top;i++){
            if(n==1)break;
            if(n%pri[i]==0){
                top2++;
                while(n%pri[i]==0){
                   n/=pri[i];
                   pow[top2]++;
                }
            }
        }
        int sum=1;
        for(i=1;i<=top2;i++){
            sum*=(pow[i]+1);
        }
        printf("%d\n",sum);
    }
    return 0;
}
巧用乘法原理:
(1)在[1000,9999]中有多少各位不相同的奇数?
首先:个位数只能取1,3,5,7,9。s1=5, s4={1,2,3……9}-1=8.s3=10-1-1=8.s2=10-1-1-1=7.s=s1*s2*s3*s4=5*8*8*7=2240.(把约束性大的放在前面计算才能得到正确的结果,一旦有了“依赖关系”就不能用乘法原理了。)
(2)在(0,10000)之间有多少个整数恰好有一位数是5?
把所有的数字统一成四位数的形式,1-->0001,12-->0012,549-->0549,那么除了5的那一位外,其他组合情况有9*9*9=729,5的分布情况有4种,所以s=4*729=2916.

@减法原理:令U是一个包含A的集合,设A'=U/A={x属于U但是不属于A},称A'是A在U中的补集。|A|=|U|-|A'|.
例子:有多少个各位数字互不相同且不为0的两位数?
用乘法原理做:9*8=72.

减法原理做:两位数字一共有90个(99-9=90),存在位数有0的有(10,20……90)9个,位数相同的两位数有(11,22,33……99)9个,所以满足条件的一共有90-9-9=72个。

@除法原理:如果知道了s中的对象数目以及各部分所含对象数目的共同值,就可以确定部分的数目。k=|s|/|s(k)|.
例子(严格的说是排列的例子): 数字1,1,1,3,8能够构造多少个不同的5位数?
s=A(5,5)/A(3,3)=5*4=20.(用乘法原理同样能分析出来)


你可能感兴趣的:(计数原理)