首页 > 代码库 > HDU 4372 Count the Buildings(组合数学-斯特林数,组合数学-排列组合)
HDU 4372 Count the Buildings(组合数学-斯特林数,组合数学-排列组合)
Count the Buildings
Now, given N, F, B, your task is to figure out how many ways all the buildings can be.
Next T lines, each line consists of three integer N, F, B, (0<N, F, B<=2000) described above.
2 3 2 2 3 2 1
2 1
题目大意:
解题思路:n个高度为1~n的房子排成一排,从前面看可以看到f个房子,从后面看可以看到b个房子,问你有多少种安排方法?
最高的房子为中间,左边有f-1个房子可以看到,右边有b-1个房子,也就是总共选出f+b-2个房子,剩余的房子在它的左边或右边,可以理解为分成了f+b-2组,且含有这个指定的顺序,看成第一类斯特林数,再从f+b-2组里面选出f-1组,答案就是:c[f+b-2][f-1]*s(n-1,f+b-2)
补充斯特林数
第一类Stirling数 s(p,k)
s(p,k)的一个的组合学解释是:将p个物体排成k个非空循环排列的方法数。
s(p,k)的递推公式: s(p,k)=(p-1)*s(p-1,k)+s(p-1,k-1) ,1<=k<=p-1
边界条件:s(p,0)=0 ,p>=1 s(p,p)=1 ,p>=0
递推关系的说明:
考虑第p个物品,p可以单独构成一个非空循环排列,这样前p-1种物品构成k-1个非空循环排列,方法数为s(p-1,k-1);
也可以前p-1种物品构成k个非空循环排列,而第p个物品插入第i个物品的左边,这有(p-1)*s(p-1,k)种方法。
第二类Stirling数 S(p,k)
S(p,k)的一个组合学解释是:将p个物体划分成k个非空的不可辨别的(可以理解为盒子没有编号)集合的方法数。
k!S(p,k)是把p个人分进k间有差别(如:被标有房号)的房间(无空房)的方法数。
S(p,k)的递推公式是:S(p,k)=k*S(p-1,k)+S(p-1,k-1) ,1<= k<=p-1
边界条件:S(p,p)=1 ,p>=0 S(p,0)=0 ,p>=1
递推关系的说明:
考虑第p个物品,p可以单独构成一个非空集合,此时前p-1个物品构成k-1个非空的不可辨别的集合,方法数为S(p-1,k-1);
也可以前p-1种物品构成k个非空的不可辨别的集合,第p个物品放入任意一个中,这样有k*S(p-1,k)种方法。
第一类斯特林数和第二类斯特林数有相同的初始条件,但递推关系不同。
解题代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; typedef long long ll; const ll mod=1000000007; const int maxn=2100; ll c[maxn][maxn],s[maxn][maxn]; //c[f+b-2][f-1]*s(n-1,f+b-2) void ini(){ for(int i=0;i<maxn;i++){ c[i][0]=c[i][i]=1; for(int j=1;j<i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod; } for(int i=0;i<maxn;i++){ s[i][0]=0; s[i][i]=1; for(int j=1;j<i;j++){ s[i][j]=(s[i-1][j-1]+(i-1)*s[i-1][j])%mod; } } } int n,f,b; int main(){ ini(); int T; scanf("%d",&T); while(T-- >0){ scanf("%d%d%d",&n,&f,&b); ll ans; if(f+b-2<maxn) ans=( c[f+b-2][f-1]*s[n-1][f+b-2] )%mod; else ans=0; cout<<ans<<endl; } return 0; }
HDU 4372 Count the Buildings(组合数学-斯特林数,组合数学-排列组合)