码迷,mamicode.com
首页 > 其他好文 > 详细

【专题】计数问题(排列组合,容斥原理,卡特兰数)

时间:2017-12-04 22:16:52      阅读:541      评论:0      收藏:0      [点我收藏+]

标签:情况   efi   组合   强制   整理   存在   序列   getchar   若是   

---下面都是学习的笔记,还没有整理,比较凌乱,有需自取吧。---

【排列组合】

<加法原理>做一件事情有n个方法,第i个方法有pi种方案,则一共有p1+p2+...+pn种方案。

<乘法原理>做一件事件有n个步骤,第i个步骤有pi种方案,则一共有p1p2...pn种方案。

乘法原理是加法原理的特殊情况,加法原理的关键是不重不漏地分类,若有重复可以考虑容斥原理。

<容斥原理>

|aUbUc|=|a|+|b|+|c|-|a∩b|-|b∩c|-|c∩a|+|a∩b∩c|

左边是所有集合的并,右边是若干集合的交集搭配,根据搭配集合个数奇加偶减。

容斥的两种形式:

1.(考虑满足条件交的集合)加所有单条件集合,减所有双条件交集合,加所有三条件交集合……

2.(考虑不满足条件交的集合)加总集,减所有不满足单条件集合,加所有不满足[双条件交]集合,加所有不满足三条件交集合。

官方表述:记f(S)表示满足集合S中至少一个条件的方案数g(S)表示满足集合S中所有条件的方案数记h(S)表示不满足集合S中所有条件的方案数

f(S)=Σ(-1)|T|-1g(T),其中T为S的非空子集

g(S)=Σ(-1)|T|h(T),其中T为S的子集。(不满足空子集的集合就是总集)

实际上,容斥原理就是已知g(s)或h(S),求解f(S)。也就是已知所有集合交(或非集合交),就可以用容斥原理求解集合并

如果考虑非集合交不方便,也可以用补集思想转化为总集-外集,其中外集就可以用另一种集合交求解了。

容斥后就不存在重复了。

例题:

1.对一个n*m的棋盘染色,每格可以是黑色或白色,要求每行每列都有黑格。

用容斥原理转化为枚举其中一些条件不满足,也就是有一些行一些列全是白格。(也可以视为用总集减去一行或一列全白格的方案数)

枚举行数i和列数j,那么选出i行j列的方案数是C(n,i)C(m,j),这i行j列全是白格的方案数是2(n-i)(m-j)

所以答案就是Σ(-1)i+jC(n,i)C(m,j)2(n-i)(m-j)。i,j>=0

时间复杂度O(nm)。

2.对一个n*m的棋盘染色,每格可以是黑色或白色,要求存在一行或一列全是黑格。

其实就是上一题的外集。

ans=Σ(-1)i+j-1C(n,i)C(m,j)2(n-i)(m-j)。(注意这是二维容斥,设置好初状态(0,1)和(1,0)为+,然后顺次容斥就可以了)。

3.对一个n*m的棋盘染色,每格可以是黑色或白色,要求存在一行和一列全是黑格。

需要满足的条件是:(A)存在一行都是黑格。(B)存在一列都是黑格。

先对A用容斥原理,答案变为Σ(-1)i-1C(n,i)*(有i行都是黑格且满足B的方案数)。

再对B用容斥原理,答案变为Σ(-1)i-1C(n,i)*Σ(-1)j-1C(m,j)*(有i行j列都是黑格的方案数)。

化简一下就是Σ(-1)i+jC(n,i)C(m,j)2(n-i)(m-j),其中i,j>=1。

时间复杂度O(nm)。

题目要求满足其中一个条件的方案数,就容斥。要求满足其中所有条件,就总集-外集

 

<抽屉原理>……

<排列>A(n,m)表示在n个数中选m个的排列数(n为下标,m为上标)

由乘法原理,每个步骤选择一个数,则分别有n,n-1,...n-m+1种选择,则可以简单地表示为:

A(n,m)=n*(n-1)*...*(n-m+1)即

A(n,m)=n!/(n-m)!

特别地,n个数的全排列是A(n,n)=n!

<组合>C(n,m)表示在n个数中选m个的组合数(n为下标,m为上标)

首先选出m个数的组合,然后把这m个数进行全排列。由乘法原理知A(n,m)=C(n,m)*A(m,m),即C(n,m)=A(n,m)/A(m,m)=n!/((n-m)!m!)。

C(n,m)=n!/((n-m)!m!)

<组合数性质>

1.C(n,0)=C(n,n)=1

2.核心性质一:C(n,k)=C(n,n-k)  组合的对称性!

3.核心性质二:C(n+1,k+1)=C(n,k)+C(n,k+1)  组合数的递推公式(杨辉三角)。

证明:n+1个数里面选择k+1个数有两类方法,若选择第一个数则转化为C(n,k),否则转化为C(n,k+1)。

4.C(n,k+1)=C(n,k)*(n-k)/(k+1)  组合数同下标递推公式

  直接利用公式变换:

    C(n,k+1)=n(n-1)...(n-k+1)(n-k)/(k+1)!

    C(n,k)=n(n-1)...(n-k+1)/k!

  两式相除得C(n,k+1)/C(n,k)=(n-k)/(k+1)

此性质可用于二项式定理(后面介绍)的预处理过程。

性质:ΣC(n,0~n)=2^n

5.重复元素问题:排列组合往往不是简单的C和A公式一写就可以解决的,因为有大量的重复元素,需要做大量去重工作。

  (1)有重复元素的全排列。

    有k个元素,其中第i个元素有ni个,求全排列个数。

    令总数为n,设答案为x。对于答案中的每个排列,对相同元素标号后,相同元素内部可以再进行全排列,所以:

    n1!n2!n3!...nk!x=n!得到x=n!/(n1!n2!n3!...nk!)

    这里和组合数的推导一样,从而得到结论:加上m个数的标号就是乘m个数的全排列,去除m个数的标号就是除以m个数的全排列

  (2)★可重复选择的组合

    问题:n个数中取k个,每个数可以取多次,求组合

     试着反过来考虑,把k个1划分成n段的方案数。

     问题转化为求解x1+x2+...+xn=k的非负整数解个数,由于0很麻烦,所以令xi=xi+1,则转化为:

     求解x1+x2+...+xn=k+n的正整数解的个数。

     想象有k+n个“1”排成一排,则问题等价于把这些“1”分成n个部分的方法数。

     隔板法:重复问题中常转化为x1+x2...+xn=X的形式,然后可以想象为在X-1个间隔中放置n-1个隔板。当隔出区间可能为0时,整体+1。

     那么在k+n-1个间隔中选n-1个,即ans=C(k+n-1,n-1)=C(n+k-1,k)。

6.正难则反的思想(补集思想),在这类数学问题中尤其常用,而且往往十分隐蔽、不易发现

技术分享图片
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
int read(){
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c==-)t=-1;
    do{s=s*10+c-0;}while(isdigit(c=getchar()));
    return s*t;
}
int min(int a,int b){return a<b?a:b;}
int max(int a,int b){return a<b?b:a;}
int abs(int x){return x>0?x:-x;}
void mins(int &a,int b){if(a>b)a=b;}
void maxs(int &a,int b){if(a<b)a=b;}
//void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
/*------------------------------------------------------------*/
const int inf=0x3f3f3f3f,MOD=1000000007;
ll n;

void gcd(ll a,ll b,ll& d,ll& x,ll& y){
    if(!b){d=a;x=1;y=0;}
     else{gcd(b,a%b,d,y,x);y-=x*(a/b);}
}
ll inv(ll a,ll n){
    ll d,x,y;
    gcd(a,n,d,x,y);
    return (x%n+n)%n;
}
ll fac[1010],fav[1010];
ll C(ll n,ll m){return fac[n]*fav[m]%MOD*fav[n-m]%MOD;}
int main(){
    fac[0]=1;fav[0]=1;
    for(int i=1;i<=1001;i++)fac[i]=fac[i-1]*i%MOD;
    for(int i=1;i<=1001;i++)fav[i]=inv(fac[i],MOD);
    
    return 0;
}
View Code

 

例题:

1.n个数字的和不超过m的的方案数。

若是等于m,则求C(n+m-1,n-1)即(n+m-1,m)。

转化为求ΣC(n+i-1,i),由组合数递推公式C(n,m)=C(n-1,m-1)+C(n-1,m)得

C(n-1,0)+C(n,1)+C(n+1,2)+C(n+2,3)+...+C(n+m-1,m)

=C(n,0)+C(n,1)+C(n+1,2)+C(n+2,3)+...+C(n+m-1,m)

=C(n+m,m)  两两一步一步凑剩一个数字。

2.袋球模型

①n个不同的球和m个不同的袋子

每个球都有m中选择,ans=m^n。

②n个相同的球和m个不同的袋子

视为将n+m个‘1‘分成m段,所以ans=C(n+m-1,m-1)。

③n个不同的球和m个相同的袋子

f[i][j]表示i球放j袋且袋子不空的方案数。

考虑一个球,可以放到j-1袋中的任意一个,也可以单独放在新的j袋中。(球不同,所以可以依次考虑每个球)

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

④n个相同的球和m个相同的袋子

强制规定袋子中的球数单调不减,则放球需要放后缀和的形式,“1 1 2”应视为[1,3]+1和[3,3]+1。

考虑当前有两种选择,新建一个空袋子作为第一个(0)或在第一个袋子放球(后缀整体+1)。

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

这样转移的正确性,可以考虑一种已有的方案,一定是通过依次增加袋子和放球来达到的,这样就可以保证不重不漏。(类似完全背包)

 

<二项式定理>

观察杨辉三角可以发现,它的第i行的数字是C(i-1,0),C(i-1,1)...C(i-1,i-1),即同一行同下标,上标从0开始递增。

由杨辉三角也可以发现组合数的两大核心性质(同行左右对称,每个数等于上面和上面左边两数之和)。

另一方面,把(a+b)^n展开的多项式系数和杨辉三角一致,由此可得

★二项式定理:技术分享图片

其中组合数对应杨辉三角也对应系数,后边对应项的内容。

也可以理解为n个括号,多少个选a,多少个选b出来的结果。

而求解系数可以使用组合数性质4。

【卡特兰数】Catalan

引用自:卡特兰数 — 计数的映射方法的伟大胜利

Catalan数列:1 1 1 2 5 14 42 132 429 1430 4862 16796

计数问题首选排列组合,其中要考虑可能暗含的斐波那契数列和卡特兰数列。

计数问题可以考虑递推形式,即把f(x)纳入公式计算范围,考虑f(i)和f(i-1)的关系等,多转换方便递推的角度就不会束手无策。

对于一类情况,只着重关注一点。

1.分治思想

一个问题A,规模为n,可以用分治的思想,先固定一个元素,然后将剩下n-1个元素拆分成两个问题,根据固定的元素位置不同,两个问题分别是(0,n-1)(1,n-2)...(n-1,0)。

对于三角剖分数问题,先固定三角形V1VnVk,根据乘法原理,包含这个三角形的有f(k)f(n-k+1)种方案。根据加法原理,f(n)=f(2)f(n-1)+f(3)f(n-2)...+f(n-1)f(2)。

对于二叉树已知叶子的形态计数问题,也可以固定根后递推。

对于出栈入栈序问题,假设第k个数最后出栈,则1~k-1有完整的出入栈,k+1~n同。

所以卡特兰数是利用分治思想递推的典例。看待的卡特兰数观点中,分治思想是主流,也更容易理解。

2.计数映射思想

将难以统计的数映射为可以统计的数,这就是计数映射思想。

卡特兰数的计数映射思想首先体现在出栈入栈序

n个数字,有多少种合法的出入序列?n=3时的合法序列之一:+1,-1,+1,+1,-1,-1

对于n个数字,就是要在2n个1中添加n个“+”,则序列总数C(2n,n)。

但是,未入栈先出栈显然不合法,但难以统计,这时考虑将不合法情况统计出来并映射。

对于一种不合法情况,我们只关注它最左边第一次前缀和为-1的情况,此时实际上是前2x+1个1中选择了x个”+“,x+1个”-“的情况。。

此时统计仍然不便,我们将这异常的2x-1个符号翻转,使异常体现在整个数列,那么这个数列就有n+1个“+”,n-1个“-”。

每一种2n个1中选择n+1个加号的情况,将其第一次前缀和为+1时的2x+1个符号翻转,就对应了一种不合法情况。

那么不合法情况的总数就是2n个1中添加n+1个“+”的序列总数C(2n,n+1)或C(2n,n-1)。将难以统计的情况转化为可以统计的情况

C(2n,n)包含了不合法情况,我们只能统计包含不合法情况的数列(即有可能前缀和<0)。

转化后的C(2n,n-1)同样包含了不合法情况,但是没关系,前缀和<0的翻转后就>0合法,未翻转的不合法也无所谓,前缀和>0第一个位置翻转后正是我们要找的不合法位置。

所以得到卡特兰数列f(n)=C(2n,n)-C(2n,n-1)=1/(n+1)*C(2n,n)

例子1:n叶子的满二叉树形态数:对于每一形态从根节点遍历,向左+1,向右-1,转化为出栈入栈序。

例子2:几何模型——计数映射的扩展。几何模型是计数映射理解的最大优势。

考虑n*n的方格,从左下到右上不跨越对角线的路径数,向右记为+1,向上记为-1,跨越对角线就是向上多于向右,则转化为卡特兰数。

考虑n*m的方格,用出栈入栈序的方法考虑,一条不合法路径只关注它第一次跨越对角线,将那处位置及其前面的路径翻转(上变左,左变上),那么就变成了到(n-1,m+1)的路径数。

那么此时计算f(n,m)=C(n+m,n)-C(n+m,n-1)。当n=m时就是Catalan数。

例子3:圆上连弦,出弦+1,入弦-1,圆上点顺序扫描即为入栈出栈序。

例子4:合法括号表达式=连乘方法数=入栈出栈序

★总结:Catalan数的题目,一般只有从几种方式看出:转化为入栈出栈序,转化为平面几何不跨线路径数,定点分治,打表。

【专题】计数问题(排列组合,容斥原理,卡特兰数)

标签:情况   efi   组合   强制   整理   存在   序列   getchar   若是   

原文地址:http://www.cnblogs.com/onioncyc/p/7979007.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!