您的位置:首页 > 其它

HDU 3625 Examining the Rooms

2017-02-10 20:47 295 查看
题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=3625

解题思路:

1.首先要知道Stirling数(斯特林数):

   第一类Stirling数:把n个可区分对象分成k个非空循环列(至少含一个数)的方法数,记为s(n,k)。

递推公式:s(n,k)=s(n-1,k-1)+(n-1)*s(n-1,k) (1<=k<=n-1)
其中:s(n,n)=1(n>=0),s(n,0)=0(n>=1) 【注意s(0,0)=1】
理解:考虑加入第n个时,有两种情况:
    1)前n-1个构成了k-1个非空循环列,第n个单独构成一个循环列,有方法数s(n-1,k-1)种
    2)前n-1个构成了k个非空循环列,第n个加入其中某个循环列的某个位置,共有(n-1)个位置可加入,有方法数(n-1)*s(n-1,k-1)种;         
   第二类Stirling数:把n个可区分对象分成k个非空集合的方法数,记为S(n,k)。

递推公式:S(n,k)=S(n-1,k-1)+k*S(n-1,k) (1<=k<=n-1)
其中:S(n,n)=1(n>=0),S(n,0)=0(n>=1)【注意S(0,0)=1】
理解:与第一类Stirling数相似,考虑加入第n个时,有两种情况:
    1)前n-1个构成了k-1个非空集合,第n个单独构成一个集合,有方法数S(n-1,k-1)种;
    2)前n-1个构成了k个非空集合,第n个加入其中某个集合,共有k个集合可加入,有方法数k*S(n-1,k-1)种;
第一类Stirling数与第二类Stirling数的区别:

区别即在“非空循环列”与“集合”,非空循环列中的数有一定顺序,而集合没有,例如【1,2,3,4,5】与【1,3,2,5,4】,它们是两个非空循环列,但却是同一个集合。

第一类Stirling数举例:

如本题。。

第二类Stirling数举例:

如把编号为1-n的n个小球放入k个一模一样(无法区别)的盒子中,每个盒子中至少有一个,问有多少种方法。(根据递推公式写一个递归函数即可解决)

2.然后考虑到这道题,实际就属于第一类Stirling数。把这n个房间分成k个非空循环列,对于每一种分法,都有唯一的放钥匙的顺序使得每个非空循环列中的房间依次按其循环列中的排列顺序打开,比如若n=5,k=2,若把这5个房间分为2个非空循环【1,2】【3,4,5】,则唯一的放钥匙顺序为【2,1,4,5,3】(第一个数字2表示第一个房间放第二个房间的钥匙,以此类推),因此可以知道当n个房间被分为k及小于k个非空循环列时的相应钥匙放置方法可以打开所有房间,但是第一间房间不能撞破,所以第一间房间不能单独形成一个循环列,只能是让其余n-1个房间构成k个非空循环列后再加入其中某个循环列的某个位置,则综合以上,该题所有能最终打开所有房间的钥匙放置方法有(n-1)*s(n-1,k)+(n-1)*s(n-1,k-1)+...+(n-1)*s(n-1,1)种,而所有钥匙放置可能有n!种,所以最终的可能为两者相除。(尽我最大努力在说清楚了。。看不明白欢迎问。。)

总结:

数学要好好学啊。。

代码如下:

#include<cstdio>
__int64 stirling1(__int64 n,__int64 k)
{
if (n==k)return 1;
else if (n>0&&k==0)return 0;
else return (n-1)*stirling1(n-1,k)+stirling1(n-1,k-1);
}
__int64 rank(__int64 a)
{
for (__int64 i=a-1;i>1;i--)a*=i;
return a;
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
int n,k;
scanf("%d %d",&n,&k);
__int64 y=0,z=rank(n);
for (int i=1;i<=k;i++)y+=(n-1)*stirling1(n-1,i);
printf("%.4f\n",(double)y/(double)z);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM HDU