@[TOC]

排列组合

定义

排列()定义:**从个不同元素中取出个元素,按照一定的顺序排成一列,叫做从个元素中取出个元素的一个排列**。
个不同元素中取出个不同元素,所有不同排列的个数称为排列种数或称排列数,记为(排列数旧版记作,都一样)。

组合()定义:**从个不同的元素中,任取个元素为一组,叫作从个不同元素中取出个元素的一个组合。**
个元素中不重复地选取个元素的一个组合。所有这样的组合的总数称为组合数,记作或


组合公式

1.证明




2.证明




3.证明

这里我们需要使用二项式展开式。
我们知道:

我们令
,得证。


4.证明

我们令:
由上一个定理知:
因为左右相等,所以我们要对应才行啊。
又因为项。
所以:

两边约掉后得证。


盒子放求问题

1.球相同,盒不同,无空盒

我们使用隔板法
这种问题就相当于是:在个球缝隙之间插入个隔板,将球分成份。
所以
代码

#include <bits/stdc++.h>
using namespace std;
int n,m;

int Factorial(int x) {
    int ans=1;
    for(int i=2;i<=x;i++) ans*=i;
    return ans;
}

int Combination(int n,int m) { return Factorial(n)/Factorial(m)/Factorial(n-m); }

int main() {
    scanf("%d %d",&n,&m);
    printf("%d",Combination(n-1,m-1));
    return 0;
}

2.球相同,盒不同,可以空盒

我们就预先在个盒中加一个虚拟球,然后问题就转换成了第一个问题。
代码

#include <bits/stdc++.h>
using namespace std;
int n,m;

int Factorial(int x) {
    int ans=1;
    for(int i=2;i<=x;i++) ans*=i;
    return ans;
}

int Combination(int n,int m) { return Factorial(n)/Factorial(m)/Factorial(n-m); }

int main() {
    scanf("%d %d",&n,&m);
    printf("%d",Combination(n+m-1,m-1));
    return 0;
}

3.球不同,盒相同,无空盒

这就是第二类斯特林数
我们定义为前个球放入个盒子的方案数。
我们把这个球放在前个盒子中,就有种方案。
我们把这个球放在第个新盒子中,就有种方案。
所以状态转移方程式为:

f[i][j]=j*f[i-1][j]+f[i-1][j-1];

我们的初始化为:

f[0][0]=1;

代码

#include <bits/stdc++.h>
using namespace std;
int n,m,f[15][15];

int main() {
    scanf("%d %d",&n,&m);
    f[0][0]=1;
    for(int i=1;i<=n;i++) {
        for(int j=0;j<=m;j++)
            f[i][j]=j*f[i-1][j]+f[i-1][j-1];
    }
    printf("%d",f[n][m]);
    return 0;
}

4.球不同,盒相同,可以空盒

这道题与第三道很像。
状态转移方程式:

for(int i=1;i<=m;i++) ans+=f[n][i];

我们就分别把个球放进前个盒子,把个球放进前个盒子...把个球放进前个盒子。
代码

#include <bits/stdc++.h>
using namespace std;
int n,m,f[15][15],ans;

int main() {
    scanf("%d %d",&n,&m);
    f[0][0]=1;
    for(int i=1;i<=n;i++) {
        for(int j=0;j<=m;j++)
            f[i][j]=j*f[i-1][j]+f[i-1][j-1];
    }
    for(int i=1;i<=m;i++) ans+=f[n][i];
    printf("%d",ans);
    return 0;
}

5.球不同,盒不同,无空盒

我们可以看做第三个问题。只是因为盒子不同,所以我们需要将盒子排个序,共有:
所以
代码

#include <bits/stdc++.h>
using namespace std;
int n,m,f[15][15],ans=1;

int main() {
    scanf("%d %d",&n,&m);
    f[0][0]=1;
    for(int i=1;i<=n;i++) {
        for(int j=0;j<=m;j++)
            f[i][j]=j*f[i-1][j]+f[i-1][j-1];
    }
    for(int i=2;i<=m;i++) ans*=i;
    printf("%d",ans*f[n][m]);
    return 0;
}

6.球不同,盒不同,可以空盒

每个球都可以选任意盒子,每个球有种选法,所以个球就有:
代码

#include <bits/stdc++.h>
using namespace std;
int n,m;

int Quick_Power(int a,int b) {
    int ans=1;
    while(b) {
        if(b&1) ans=ans*a;
        b>>=1,a=a*a;
    }
    return ans;
}

int main() {
    scanf("%d %d",&n,&m);
    printf("%d",Quick_Power(m,n));
    return 0;
}

7.球相同,盒相同,无空盒

我们定义个球个盒子的方案数。
则:

for(int k=1;k<=j;k++)
    f[i][j]=(f[i][j]+f[i-j][k])%MOD;

我们先铺个球分别到个盒子,然后剩下个球随便放。
代码

#include <bits/stdc++.h>
using namespace std;
const int MOD=12345;
int n,m,f[1005][1005];

int main() {
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++) f[i][1]=f[i][i]=f[i][i-1]=1;

    for(int i=1;i<=n;i++) {
        for(int j=2;j<=min(m,i-2);j++) {
            for(int k=1;k<=j;k++) 
                f[i][j]=(f[i][j]+f[i-j][k])%MOD;
        }
    }
    printf("%d",f[n][m]);
    return 0;
}

8.球相同,盒相同,可以空盒

我们就放个虚拟球分别到个盒子,然后个球随便放。
代码

#include <bits/stdc++.h>
using namespace std;
const int MOD=12345;
int n,m,f[2005][1005];

int main() {
    scanf("%d %d",&n,&m);
    n+=m;
    for(int i=1;i<=n;i++) f[i][1]=f[i][i]=f[i][i-1]=1;

    for(int i=1;i<=n;i++) {
        for(int j=2;j<=min(m,i-2);j++) {
            for(int k=1;k<=j;k++) 
                f[i][j]=(f[i][j]+f[i-j][k])%MOD;
        }
    }
    printf("%d",f[n][m]);
    return 0;
}

总结