Little Pony and Alohomora Part 3 [HihoCoder 1075]

描述

一日,崔克茜来到小马镇表演魔法。

其中有一个节目是开锁咒:舞台上有 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

分析

我们设dp [i][j]
那么前一个状态为 dp [i-1][j-use],即用j-use把钥匙打开前i-1个阶段的盒子一共有的方法数
也就是说打开第i个阶段的盒子用了use个钥匙,一共有  C(cnt,use)种方法,cnt为第i个阶段一共有cnt个待打开的盒子

那么根据乘法原理
dp[i][j]=dp[i-1][j-use]*C(cnt,use) ,
但是前一个状态不唯一,也就是use的值可以变化,所以要方法累加 即dp[i][j]+=dp[i-1][j-use]*C(cnt,use).  这里dp[i][j]是未知的,要求它就必须知道dp[i-1][j-use],也就是用已知的状态去推出未知的状态。

 

代码

 

 1 #include <iostream>  
 2 #include <stdio.h>  
 3 #include <string.h>  
 4 #include <vector>  
 5 #include <iomanip>  
 6 using namespace std;  
 7 int n,k;  
 8 int match[320];  
 9 double c[320][320];  
10 bool vis[320];  
11 double dp[320][320];  
12 vector<int>loop;     
13 void getComb()  
14 {  
15     for(int i=0;i<=300;i++)  
16     {  
17         c[i][0]=c[i][i]=1.0;  
18         for(int j=1;j<i;j++)  
19             c[i][j]=c[i-1][j]+c[i-1][j-1];  
20     }  
21 }  
22       
23 int main()  
24 {  
25     getComb();  
26     int t;  
27     cin>>t;  
28     while(t--)  
29     {  
30         cin>>n>>k;  
31         for(int i=1;i<=n;i++)  
32             cin>>match[i]; 
33         loop.clear();
34         memset(vis,0,sizeof(vis));  
35         for(int i=1;i<=n;i++)//求循环节  
36         {  
37             if(vis[i])  continue;  
38             int cnt=0,cur=i;  
39             while(!vis[cur])  
40             {  
41                 cnt++;  
42                 vis[cur]=1;  
43                 cur=match[cur];  
44             }  
45             loop.push_back(cnt);  
46         }  
47               
48         int num=loop.size();  
49         if(k<num)  
50         {  
51             printf("%.9lf\n",0.0);  
52             continue;  
53         }  
54               
55         memset(dp,0,sizeof(dp));  
56         dp[0][0]=1.0;  
57         for(int i=0;i<num;i++)  
58         {  
59             for(int j=0;j<k;j++)  
60             {  
61                 if(dp[i][j]==0)  continue;  
62                 for(int use=1;use<=loop[i]&&j+use<=k;use++)  
63                     dp[i+1][j+use]+=dp[i][j]*c[loop[i]][use]; 
64             }  
65         }  
66         printf("%.9lf\n",dp[num][k]/c[n][k]);  
67     }  
68     return 0;
69 }  查看代码
点击查看代码

 

转载于:https://www.cnblogs.com/ibilllee/p/7649918.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值