Hihocoder #1075 : 开锁魔法III (组合数学+动态规划)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
把造数据的东西和对拍器都发上来吧。
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
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 }
能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 }
以上是关于Hihocoder #1075 : 开锁魔法III (组合数学+动态规划)的主要内容,如果未能解决你的问题,请参考以下文章
Hihocoder #1075 : 开锁魔法III (组合数学+动态规划)
Little Pony and Alohomora Part 3 [HihoCoder 1075]