1. 程式人生 > >HihoCoder 1075 開鎖魔法III(概率DP+組合)

HihoCoder 1075 開鎖魔法III(概率DP+組合)

全部 小數 概率dp break turn 表示 num 小馬 clas

描述

一日,崔克茜來到小馬鎮表演魔法。

其中有一個節目是開鎖咒:舞臺上有 n 個盒子,每個盒子中有一把鑰匙,對於每個盒子而言有且僅有一把鑰匙能打開它。初始時,崔克茜將會隨機地選擇 k 個盒子用魔法將它們打開。崔克茜想知道最後所有盒子都被打開的概率,你能幫助她回答這個問題嗎?

輸入

第一行一個整數 T (T ≤ 100)表示數據組數。 對於每組數據,第一行有兩個整數 nk (1 ≤ n ≤ 300, 0 ≤ k ≤ n)。 第二行有 n

個整數 ai,表示第 i 個盒子中,裝有可以打開第 ai 個盒子的鑰匙。

輸出

對於每組詢問,輸出一行表示對應的答案。要求相對誤差不超過四位小數。

樣例輸入

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+組合)