题目描述:有n个盒子,装着东西,m个人取盒子,每次取一个盒子打开,每个盒子的第一个打开者拿走物品,而放回空盒子,以后的人打开了空盒子就没有东西拿。那么问东西被拿走的期望是多少。
首先这是一个放回的概率,即盒子的总数一直没有变化,都是n,而一个人拿走了东西,下一个人拿到东西的概率就被-1/n 因为一个人最多只能带走一件东西,而盒子总量没有变化,假设第i个人拿走东西的概率是Y[ I ] 没拿走东西的概率是N[ i ],显然两者是对立事件(加和为1),那么可以找到递推关系式:
Y[ i ] = N[ i -1 ] *Y[ i-1 ] + Y[ i - 1 ] * (Y [ i - 1 ] - 1/n) // 即两种情况:1.前面一个人没有拿走东西,此时能拿到东西的概率同前一个人不变 2.前面一人拿走了东西,现在i拿到东西的概率就比前一人下降1/n
N[ i ]=1-Y[ i ]
那么算出每一个人拿到东西的概率,乘以拿到东西的件数(1件),就是最后的结果
其实还有一种更方便的算法:每个盒子一直没被选中的概率是P=((n-1)/n)^m,那么被选中的概率是1-P,盒子数是n,每个盒子的权值是1,所以最后的结果是 n*( 1 - p ) * 1
这题恶心在卡最后的输出,一定是printf("%.10lf",ret) 之前没用。10lf果断WA到死,还一度对第二种方法产生怀疑,弱狗一只飘过,但好处是,两个代码我都实现了。
第一种方法:
#include<cstdio> using namespace std; #define maxn 1000005 double Y[maxn],N[maxn]; int main(){ int i,j,n,m; while(scanf("%d %d",&n,&m)!=EOF){ Y[1]=1; N[1]=0; double ret=1; for(i=2;i<=m;i++){ Y[i]=Y[i-1]*N[i-1]+Y[i-1]*(Y[i-1]-1.0/n); N[i]=1-Y[i]; ret+=Y[i]; } printf("%.10lf\n",ret); } }
第二种方法:
#include<cstdio> using namespace std; double ac(int n,int m){ double now=(n-1.0)/n; double ret=n; while(m){ if(m&1) ret*=now; m>>=1; now*=now; } return n-ret; } int main(){ int n,m; while(scanf("%d %d",&n,&m)!=EOF){ printf("%.10lf\n",ac(n,m)); } return 0; }