组合数学 —— 组合数
【概念】
1.組合
從 n 個(gè)元素的集合 S 中,無(wú)序的選出 r 個(gè)元素,叫做 S 的一個(gè) r 組合。
如果兩個(gè)組合中,至少有一個(gè)元素不同,它們就被認(rèn)為是不同的組合。
2.不可重組合數(shù)
所有不同組合的個(gè)數(shù),叫做組合數(shù),記作:?或?
由于每一種組合都可以擴(kuò)展到 r!種排列,而總排列為 A(n,r) ,所以組合數(shù)
特別的,C(n,0)=1
3.可重復(fù)組合數(shù)
從 n 個(gè)不同的元素中,無(wú)序的選出 r 個(gè)元素組成一個(gè)組合,且允許這 r 個(gè)元素重復(fù)使用,則稱(chēng)這樣的組合為可重復(fù)組合。
其組合數(shù)記為:
4.不相鄰組合數(shù)
從 A={1,2,...,n} 中選取 m 個(gè)不相鄰的組合,其組合數(shù)為:
例題:
①?一班有10名同學(xué),二班有8名同學(xué),現(xiàn)每個(gè)班級(jí)要選出2名學(xué)生參加一個(gè)座談會(huì),求有多少種選法?
根據(jù)組合數(shù)與乘法原理,共有:C(10,2)*C(8,2)=1260 種
② 某班有10名同學(xué),有4名女同學(xué),現(xiàn)要選出3名學(xué)生,其中至少有一名女同學(xué),求有多少種選法?
根據(jù)組合數(shù)與加法原理,共有:C(4,1)*C(6,2)+C(4,2)*C(6,1)+C(4,3)*C(6,0)=60+36+4=100 種
【組合數(shù)常用公式】
1)
2)
3)
4)(二項(xiàng)式定理)
特殊展開(kāi):
5)?為奇數(shù)時(shí)有 n&m=n?
【求組合數(shù)的方法】
首先 C(n,m) 的值一定是自然數(shù),因?yàn)檫B續(xù) m?個(gè)自然數(shù)的積一定被 m! 整除,因此求 C(n,m) 的值關(guān)鍵在于如何避免做除法。
1.遞歸計(jì)算
利用公式??來(lái)遞歸的計(jì)算組合數(shù)
LL cal(LL n,LL k){if(n<k||k==0)return 0;if(n==k||k==1)return 1;return cal(n-1,k-1)+k*cal(n-1,k); } int main(){LL n,k;cin>>n>>k;cout<<cal(n,k)<<endl;return 0; }2.楊輝三角打表
利用公式?,將計(jì)算 C(n,r) 的過(guò)程化為加法來(lái)做,由于二項(xiàng)式展開(kāi)系數(shù)的與楊輝三角一致,故該方法的實(shí)質(zhì)就是求楊輝三角第 n 行,第 r 列上的數(shù)。
int f[N][N]; int main() {f[0][0]=1;for(int i=1;i<=N-1;i++)for(int j=1;j<=i+1;j++)f[i][j]=f[i-1][j]+f[i-1][j-1];int n,r;scanf("%d%d",&n,&r);printf("%d\n",f[n+1][r+1]);return 0; }3.公式化簡(jiǎn)打表
用 ,由于除以 m,因此相對(duì)沒(méi)那么容易越界
int C[N]; void calculate(int n,int m){//C[i]即為C(n,i)的值C[0]=1;for(int i=1;i<=n;i++)C[i]=C[i-1]*(n-i+1)/i; }4.約分求重?cái)?shù)
約分之后,分母即會(huì)變?yōu)?1,借此將除法化為乘法,約分方法是計(jì)算 1 到 n 之間的任意一個(gè)質(zhì)數(shù)在 C(n,r) 的重?cái)?shù)。
具體做法是對(duì)分子分母上的每個(gè)數(shù)分解質(zhì)因子,用一個(gè)數(shù)組 C[] 來(lái)記錄重?cái)?shù),若分子上的數(shù)分解一個(gè)質(zhì)因子 p,則 C[p]++,反之若分母上的數(shù)分解出質(zhì)因子 p,則 C[p]--,最后將每個(gè)質(zhì)因子按其重?cái)?shù)連乘即可。
將公式化為?,通過(guò)直接計(jì)算質(zhì)數(shù) p 在 n! 中的重?cái)?shù)而得到數(shù)組 C[],質(zhì)數(shù) p 在自然數(shù) n 中的重?cái)?shù)是指自然數(shù) n 的質(zhì)因數(shù)分解式質(zhì)數(shù) p 出現(xiàn)的次數(shù),質(zhì)數(shù) p 在 n! 的重?cái)?shù)為:?,根據(jù)公式:,可以遞推的求出 p 在 n!中的重?cái)?shù)。
例如:72=2*2*2*3*3,質(zhì)數(shù) 2 在 72 的重?cái)?shù)是 3,質(zhì)數(shù) 3 在 72 的重?cái)?shù)是 2;n=1000,p=3時(shí),有 1000 div 3+1000 div 9+1000 div 27+1000 div 81+1000 div 243+1000 div 729=333+111+37+12+4+1=498,因此 1000!能被 3^498 整除,但不能被 3^499 整除,使用遞推公式后,有:333 div 3=111 ,111 div 3=37,37 div 3=12,12 div 3=4,4 div 3=1
程序?qū)崿F(xiàn)時(shí),先求出 1 到 n 間所有質(zhì)數(shù),再對(duì)每個(gè)質(zhì)數(shù)求重?cái)?shù),從而計(jì)算從 n-r+1 到 n 的因子的重?cái)?shù)與從 1 到 r 的因子的重?cái)?shù),前者減去后者,C[i] 中所存儲(chǔ)的即為約分后質(zhì)數(shù)因子的重?cái)?shù),再利用高精度加法,將答案存儲(chǔ),最后倒序輸出即可。
#include<cstdio> #include<cstring> #include<vector> const int N=30000; vector<int> prime,C; bool vis[N]; int res[10]; void Get_Prime() {memset(vis,true,sizeof(vis));for(int i=2;i<=N;i++){if(vis[i]){prime.push_back(i);//存儲(chǔ)質(zhì)數(shù)C.push_back(0);//當(dāng)前質(zhì)數(shù)的重?cái)?shù)為0for(int j=i*i;j<=N;j+=i)//篩除所有以i為因子的數(shù)vis[j]=false;}} } void Add(int n,int p)//記錄重?cái)?shù)個(gè)數(shù) {for(int i=0;i<prime.size()&&prime[i]<=n;i++){while(!(n%prime[i])){n/=prime[i];C[i]+=p;}} } int main() {Get_Prime();//打表獲取質(zhì)數(shù)int n,r;scanf("%d%d",&n,&r);if(r>n-r)//根據(jù)公式C(n,r)=C(n,n-r)簡(jiǎn)化計(jì)算r=n-r;for(int i=0;i<r;i++){Add(n-i,1);//將n-r+1到n的因子加到C中去Add(i+1,-1);//將1到r的因子從C中減去}memset(res,0,sizeof(res));res[0]=1;for(int i=0;i<prime.size();i++)//枚舉所有質(zhì)數(shù){for(int j=0;j<C[i];j++)//枚舉對(duì)應(yīng)質(zhì)數(shù)的重?cái)?shù){for(int k=0;k<10;k++)res[k]*=prime[i];for(int k=0;k<10;k++)//高精存儲(chǔ)答案{if(k<9)res[k+1]+=res[k]/10;res[k]%=10;}}}for(int i=9;i>=0;i--)printf("%d",res[i]);printf("\n");return 0; }【例題】
- Rooks(LightOJ-1005)(排列+組合):點(diǎn)擊這里
- Wall Painting(HDU-4810)(楊輝三角組合數(shù)打表+二進(jìn)制枚舉):點(diǎn)擊這里
- Combinations(POJ-1306)(公式化簡(jiǎn)法求組合數(shù)):點(diǎn)擊這里
- Binomial Showdown(POJ-2249)(公式化簡(jiǎn)法求組合數(shù)):點(diǎn)擊這里
- 集合的劃分(信息學(xué)奧賽一本通-T1315)(遞歸法求組合數(shù)):點(diǎn)擊這里
總結(jié)
以上是生活随笔為你收集整理的组合数学 —— 组合数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 训练日志 2019.1.23
- 下一篇: 训练日志 2019.8.23