HDU 3625 Examining the Rooms:第一類Stirling數

Leohh發表於2017-08-17

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=3625

題意:

  有n個房間,每個房間裡放著一把鑰匙,對應能開1到n號房間的門。

  除了1號門,你可以踹開任意一扇門(不用鑰匙),但你最多隻能踹k次。

  問你能將所有門開啟的概率。

 

題解:

  · P(開啟所有門) = 能開啟所有門的鑰匙放置情況數 / 鑰匙放置的總情況數

  · 鑰匙放置的總情況數 = n!

  

  那麼考慮下能開啟所有門的鑰匙放置情況數。。。

  由於每個房間裡有且只有一把鑰匙,所以如果將每個房間連向房間內鑰匙對應的房間,會得到一個有向圖,並且由若干個獨立的環組成。

  

  所以,只要將一個環內的任意一扇門踹開,就能開啟這個環上的所有房間。

  如果不考慮1號門,那麼要算的就是n個元素組成1~k個環排列的情況數。

  第一類Stirling數的定義啊!

  遞推式:s(n,k) = s(n-1,k-1) + (n-1)*s(n-1,k)

  

  So...

  · 能開啟所有門的鑰匙放置情況數 = ∑S(n,i) (1<=i<=k)

  

  考慮到1號門不能踹,也就是1不能獨立成環,它的情況數就等於用剩下n-1的元素組成i-1個環的情況數。

  · 能開啟所有門的鑰匙放置情況數 = ∑( S(n,i)-S(n-1,i-1) )

 

  所以答案為:∑( S(n,i)-S(n-1,i-1) ) / n!  (1<=i<=k)

 

AC Code:

 1 // s(n,k) = s(n-1,k-1) + (n-1)*s(n-1,k)
 2 // s(n,0) = 0    s(n,n) = 1
 3 // P = sigma(s[n][i]-s[n-1][i-1])/fact(n)    1<=i<=k
 4 
 5 #include <iostream>
 6 #include <stdio.h>
 7 #include <string.h>
 8 #define MAX_N 25
 9 #define MAX_K 25
10 
11 using namespace std;
12 
13 int n,k,t;
14 long long sum;
15 long long s[MAX_N][MAX_K];
16 long long fact[MAX_N];
17 
18 void stirling()
19 {
20     memset(s,0,sizeof(s));
21     for(int i=1;i<MAX_N;i++)
22     {
23         s[i][i]=1;
24         for(int j=1;j<i;j++)
25         {
26             s[i][j]=s[i-1][j-1]+(i-1)*s[i-1][j];
27         }
28     }
29 }
30 
31 void cal_fact()
32 {
33     fact[0]=1;
34     for(int i=1;i<MAX_N;i++)
35     {
36         fact[i]=fact[i-1]*i;
37     }
38 }
39 
40 int main()
41 {
42     stirling();
43     cal_fact();
44     cin>>t;
45     for(int cas=1;cas<=t;cas++)
46     {
47         cin>>n>>k;
48         sum=0;
49         for(int i=1;i<=k;i++)
50         {
51             sum+=s[n][i]-s[n-1][i-1];
52         }
53         printf("%.4f\n",(double)sum/fact[n]);
54     }
55 }

 

相關文章