HihoCoder 1075 開鎖魔法III(概率DP+組合)
阿新 • • 發佈:2018-03-29
全部 小數 概率dp break turn 表示 num 小馬 clas
個整數 ai,表示第 i 個盒子中,裝有可以打開第 ai 個盒子的鑰匙。
描述
一日,崔克茜來到小馬鎮表演魔法。
其中有一個節目是開鎖咒:舞臺上有 n 個盒子,每個盒子中有一把鑰匙,對於每個盒子而言有且僅有一把鑰匙能打開它。初始時,崔克茜將會隨機地選擇 k 個盒子用魔法將它們打開。崔克茜想知道最後所有盒子都被打開的概率,你能幫助她回答這個問題嗎?
輸入
第一行一個整數 T (T ≤ 100)表示數據組數。 對於每組數據,第一行有兩個整數 n 和 k (1 ≤ n ≤ 300, 0 ≤ k ≤ n)。 第二行有 n
輸出
對於每組詢問,輸出一行表示對應的答案。要求相對誤差不超過四位小數。
樣例輸入
4 5 1 2 5 4 3 1 5 2 2 5 4 3 1 5 3 2 5 4 3 1 5 4 2 5 4 3 1
樣例輸出
0.000000000 0.600000000 0.900000000 1.000000000
1,每個盒子都有一個入度和一個出度,以之前二分圖拆點的經驗來看,必然會形成很多個環。
2,每個環至少選擇一個盒子。
3,每個環至少選擇一個盒子的組合數,聯想到母函數,組合數。
4.自由YY。可以DP,但是誤差可能大一些。可以全部求出來再除,這樣誤差小一些。
(ps:學會了母函數再搞組合是要多一分靈感!彎的four)
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<cmath> using namespace std; const int maxn=310; double c[maxn][maxn]; double a[maxn],b[maxn]; int loop[maxn],num[maxn],cnt,laxt[maxn],n; void init() { cnt=0; for(int i=0;i<=n;i++){ a[i]=b[i]=num[i]=loop[i]=laxt[i]=0; } } void getc() { int i,j; for(i=1;i<=300;i++){ c[i][0]=c[i][i]=1.0; for(j=1;j<i;j++) c[i][j]=c[i-1][j]+c[i-1][j-1]; } return ; } void geta() { int i,j,k; for(i=1;i<=num[1];i++) a[i]=c[num[1]][i],b[i]=0.0; for(i=2;i<=cnt;i++){ for(j=0;j<=n;j++) for(k=1;k<=num[i];k++) if(j+k<=n) b[j+k]+=a[j]*c[num[i]][k];//不是+1 for(j=0;j<=n;j++){ a[j]=b[j]; b[j]=0; } } } int main() { int i,j,T,k; scanf("%d",&T); getc();//組合數C while(T--){ scanf("%d%d",&n,&k); init(); for(i=1;i<=n;i++) scanf("%d",&laxt[i]); for(i=1;i<=n;i++){//分組 if(!loop[i]){ ++cnt; for(j=i;;j=laxt[j]){ if(loop[j]) break; loop[j]=cnt; num[cnt]++; } } } geta();//保證每個環至少一個的母函數 printf("%.9lf\n",a[k]/c[n][k]); } return 0; }
HihoCoder 1075 開鎖魔法III(概率DP+組合)