Hihocoder #1075 : 开锁魔法III (组合数学+动态规划)

题目链接

题目大意:有n个箱子,每个箱子中放着一个箱子的钥匙,可以用魔法打开k个箱子,问最终能打开所有箱子的概率是多少。

思路:首先我们想到,如果一组箱子的打开目标能构成一个环,那么这个箱子中只要打开一个就随意了。问概率的话,最好想的做法就是用A事件发生的次数/事件总数。事件总数很好求,就是在n个箱子中选择k个打开:C[n][k].

然后考虑有多少种合法的打开方式,我开始想的是直接用组合数学乱搞,乘法原理,设一共有tot个环,每个环的大小分别是a1,a2,a3...atot;计算的时候先a1*a2*...*atot,然后如果tot<k,再乘上C[n][k-tot];感觉好像很正确呀,但是姜爷(faebdc)表示我肯定想错了。

再思考下,发现会有很多重复的情况,关键就在于最后的C[n][k-tot]不确定性太大,实际上看一下数据范围(n<=300),如果要真的直接乘起来就能得到答案,那完全可以把数据范围增加到10000. 我们考虑将统计过程细节化,记f[i][j]表示前i个环中开了j个箱子保证每个环至少开一个的方案数,那么只要f[i][j]!=0,就可以向后转移,枚举第i+1个环开几个箱子记作ad,那么f[i+1][j+ad]+=f[i][j]*C[sz[i+1]][k],也就是第i+1个环中选ad个的方案数*前i个环中选j个的方案数(这里是一个分步计数,要用乘法原理)的和(这是分类计数,用ad讨论,使用加法原理)。

程序不知道怎么,在hiho上过不了,和标称对拍一点错也没有。路过的大神帮忙看下吧。谢谢了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 #define N 310
 7 int n,k,to[N],tot,sz[N],T; bool visit[N];
 8 double f[N][N],C[N][N];
 9 void Init()
10 {    C[1][0]=1.0,C[1][1]=1.0;
11     for (int i=2;i<=300;i++)
12     {    C[i][0]=1.0;
13         for (int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
14     }
15 }
16 int main()
17 {    Init();cin>>T;
18     while (T--){
19     scanf("%d%d",&n,&k);for(int i=1;i<=n;i++)scanf("%d",&to[i]);
20     memset(sz,0,sizeof(sz));memset(visit,0,sizeof(visit));
21     for (int i=1;i<=n;i++)
22     if (!visit[i])
23     {    int cnt=0,cur=i;for(;!visit[cur];visit[cur]=1,cnt++,cur=to[cur]);
24         sz[++tot]=cnt;
25     }
26     if (k<tot){printf("%.9lf\n",0.0);continue;}
27     memset(f,0,sizeof(f)); f[0][0]=1.0;
28     for (int i=0;i<tot;i++)
29         for (int j=0;j<k;j++)
30              if (f[i][j])
31              {    for (int ad=1;ad<=sz[i+1]&&ad+j<=k;ad++)
32                     f[i+1][j+ad]+=f[i][j]*C[sz[i+1]][ad];
33              }
34     printf("%.9lf\n",f[tot][k]/C[n][k]);
35     }
36     return 0;
37 }
38     

WA Code

把造数据的东西和对拍器都发上来吧。

 1 #include<iostream>
 2 #include<ctime>
 3 #include<cstdio>
 4 #include<algorithm>
 5 using namespace std;
 6 bool visit[100000];
 7 int main()
 8 {    freopen("data.in","w",stdout);
 9     srand(time(0));
10     cout<<1<<endl;
11     int n=300;
12     cout<<n<<endl;int sum=0;
13     while (sum<n)
14     {    int a=rand();
15         a=a%n+1;
16         if (visit[a]==0)
17         {    sum++;
18             visit[a]=1;
19             cout<<a<<‘ ‘;
20         }
21     }
22     return 0;
23 }
24
25         

Make Data

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstdlib>
 5 using namespace std;
 6 int main()
 7 {    for (int i=1;i<=1000;i++)
 8     {    system("hiho1075data");
 9         system("hiho1075 < data.in >a.out");
10         system("hiho1075r < data.in >b.out");
11         system("fc a.out b.out");
12     }
13     return 0;
14 }

Duipai

能AC的东东(转自http://www.cnblogs.com/wzj-is-a-juruo/p/4802655.html)

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<queue>
 4 #include<cmath>
 5 #include<cstring>
 6 #include<algorithm>
 7 #define rep(i,s,t) for(int i=s;i<=t;i++)
 8 #define dwn(i,s,t) for(int i=s;i>=t;i--)
 9 #define ren for(int i=first[x];i;i=next[i])
10 using namespace std;
11 inline int read() {
12     int x=0,f=1;char c=getchar();
13     for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1;
14     for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘;
15     return x*f;
16 }
17 const int maxn=310;
18 int n,k,size[maxn],cnt,v[maxn],vis[maxn];
19 double f[maxn][maxn],C[maxn][maxn];
20 int main() {
21     int T=read();
22     C[0][0]=1;
23     rep(i,0,300) rep(j,0,i) C[i+1][j+1]+=C[i][j],C[i+1][j]+=C[i][j];
24     while(T--) {
25         n=read();k=read();cnt=0;
26         memset(size,0,sizeof(size));
27         memset(vis,0,sizeof(vis));
28         rep(i,1,n) v[i]=read();
29         rep(i,1,n) if(!vis[i]) {
30             cnt++;int j=i;
31             do size[cnt]++,vis[j]=1,j=v[j];while(j!=i);
32         }
33         memset(f,0,sizeof(f));
34         f[1][0]=1.0;int cur=0;
35         rep(i,1,cnt) {
36             rep(j,0,cur) rep(k0,1,size[i]) f[i+1][j+k0]+=f[i][j]*C[size[i]][k0];
37             cur+=size[i];
38         }
39         printf("%.6lf\n",f[cnt+1][k]/C[n][k]);
40     }
41     return 0;
42 }

AC Code

时间: 2024-08-09 06:15:32

Hihocoder #1075 : 开锁魔法III (组合数学+动态规划)的相关文章

[ACM] hihoCoder 1075 开锁魔法III (动态规划,组合数学)

描述 一日,崔克茜来到小马镇表演魔法. 其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它.初始时,崔克茜将会随机地选择 k 个盒子用魔法将它们打开.崔克茜想知道最后所有盒子都被打开的概率,你能帮助她回答这个问题吗? 输入 第一行一个整数 T (T ≤ 100)表示数据组数. 对于每组数据,第一行有两个整数 n 和 k (1?≤?n?≤?300,?0?≤?k?≤?n). 第二行有 n 个整数 ai,表示第 i 个盒子中,装有可以打开第 a

HihoCoder 1075 开锁魔法III(概率DP+组合)

描述 一日,崔克茜来到小马镇表演魔法. 其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它.初始时,崔克茜将会随机地选择 k 个盒子用魔法将它们打开.崔克茜想知道最后所有盒子都被打开的概率,你能帮助她回答这个问题吗? 输入 第一行一个整数 T (T ≤ 100)表示数据组数. 对于每组数据,第一行有两个整数 n 和 k (1 ≤ n ≤ 300, 0 ≤ k ≤ n). 第二行有 n 个整数 ai,表示第 i 个盒子中,装有可以打开第 a

hihoCoder - 1075 开锁魔法III

Description 一日,崔克茜来到小马镇表演魔法. 其中有一个节目是开锁咒:舞台上有 \(n(n\le 300)\) 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它.初始时,崔克茜将会随机地选择 \(k\) 个盒子用魔法将它们打开.崔克茜想知道最后所有盒子都被打开的概率,你能帮助她回答这个问题吗? Solution 这 gay 题. 把 \(i\) 连向 \(a_i\) ,就成了若干简单环.只要最开始打开一个环内任何一个点,就可以打开整个环. 令每个环大小为 \(s

[dp+组合数学] hihocoder 1075 开锁魔法III

题意: 中文题~ 思路: 首先需要处理的就是把所有的环找出来 环的意思就是这个环里面只要有一个盒子被打开了,其他盒子就都被打开了. 比如样例.就是有两个环(1,2,5) 和 (3,4)并记录环内盒子的总数. 这样就转换成了这样的一个问题了. 给n堆东西,每堆有ai个,问现在取k次,保证每堆至少取1个的方案数. 然后总的方案数是C[n][k].概率一除就好了. 那么这个方案数怎么求呢. 我们就要设dp[i][j]代表花j个钥匙开前i个环的方案数. 那么转移的话就是 dp[i][j]=Σ dp[i-

hihocoder 1075 开锁魔法III(置换+DP)

这题先预处理数组有多少个置换,这样的话,每个置换最少要被选到一次,最多就是置换长度的次数,利用这些置换进行DP,和背包一样的,每个置换当成一个物品,选择的概率很容易算出,利用这点进行状态转移即可算出种数,最后在除上总情况数就可以算出概率 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int N

#1075 : 开锁魔法III

描述 一日,崔克茜来到小马镇表演魔法. 其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它.初始时,崔克茜将会随机地选择 k 个盒子用魔法将它们打开.崔克茜想知道最后所有盒子都被打开的概率,你能帮助她回答这个问题吗? 输入 第一行一个整数 T (T ≤ 100)表示数据组数. 对于每组数据,第一行有两个整数 n 和 k (1 ≤ n ≤ 300, 0 ≤ k ≤ n). 第二行有 n 个整数 ai,表示第 i 个盒子中,装有可以打开第 a

Hiho #1075: 开锁魔法III

Problem Statement 描述 一日,崔克茜来到小马镇表演魔法. 其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它.初始时,崔克茜将会随机地选择 k 个盒子用魔法将它们打开.崔克茜想知道最后所有盒子都被打开的概率,你能帮助她回答这个问题吗? 输入 第一行一个整数$T$ ($T \leq 100$)表示数据组数. 对于每组数据,第一行有两个整数$n$和$k$ ($1 \leq n \leq 300, 0 \leq k \leq 

哈理工2249开锁魔法 概率dp

开锁魔法II Time Limit: 3000 MS Memory Limit: 256000 K Total Submit: 23(18 users) Total Accepted: 18(16 users) Rating:  Special Judge: No Description 一日,崔克茜来到小马镇表演魔法. 其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它.崔克茜可以通过魔法,暴力打开一个盒子.但是崔克茜最多只可以使用 k

开锁魔法II 哈尔滨理工大学第五届ACM程序设计竞赛

规律:a[i][j]=     1/i * a[i-1][j-1]      +      (i-1)/i * a[i-1][j];  (少一个盒子时使用j-1 次魔法的概率)   (少一个盒子时使用j次魔法的概率) 公式推导如下: 设a[i][j]为打开i个盒子正好需要j次魔法的情况. ① 1->1 ② 1->1 , 2->2;        两次 1->2 , 2->1;        一次 ③ 1->1 , 2->2 , 3->3;     三次 1-