生活随笔
收集整理的這篇文章主要介紹了
hihocoder 1075 : 开锁魔法III
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
描述
一日,崔克茜來到小馬鎮表演魔法。
其中有一個節目是開鎖咒:舞臺上有 n 個盒子,每個盒子中有一把鑰匙,對于每個盒子而言有且僅有一把鑰匙能打開它。初始時,崔克茜將會隨機地選擇 k 個盒子用魔法將它們打開。崔克茜想知道最后所有盒子都被打開的概率,你能幫助她回答這個問題嗎?
解題報告:
用時:20min,1A
我們按\(i\)到\(ai\)連邊發現,在同一環內的我們選取任意一個即可
所以我們統計這樣的連通子圖的個數\(m\),即每一個子圖的節點數,所以我們只要保證每一個子圖至少選到一個即可,所以我們DP方案數:
\(f[i][j]\)表示前i個子圖中選了j個點的方案數
\(f[i][j]+=f[i-1][j-l]*c[s[i]][l]\)
\(s[i]\)表示i這個子圖的大小,c為組合數,這里我么要保證每一個至少都選一個那就限制j-l>=i-1即可,最后答案就是\(f[m][k]/c[n][k]\)
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=3e2+5;
int n,k,s[N],m=0,a[N];double f[N][N],c[N][N];bool vis[N];
void prework(){for(int i=0;i<N;i++){c[i][0]=1;for(int j=1;j<=i;j++)c[i][j]=c[i-1][j-1]+c[i-1][j];}
}
void work()
{m=0;scanf("%d%d",&n,&k);for(int i=1;i<=n;i++)scanf("%d",&a[i]),vis[i]=false;int x,t=0;for(int i=1;i<=n;i++){if(vis[i])continue;x=i;t=0;while(!vis[x]){vis[x]=true;x=a[x];t++;}s[++m]=t;}memset(f,0,sizeof(f));f[0][0]=1;for(int i=1;i<=m;i++){for(int j=1;j<=k;j++)for(int l=1;l<=s[i] && j-l>=i-1;l++){f[i][j]+=f[i-1][j-l]*c[s[i]][l];}}double ans=(double)f[m][k]/(c[n][k]*1.0);printf("%.4lf\n",ans);
}int main()
{int T;cin>>T;prework();while(T--)work();return 0;
}
轉載于:https://www.cnblogs.com/Yuzao/p/7517881.html
總結
以上是生活随笔為你收集整理的hihocoder 1075 : 开锁魔法III的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。