題目連結: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 }