题意:一个包里有无穷多个巧克力,每次从包里拿出一个巧克力放在桌子上。若桌子上出现两个相同的巧克力则立即吃掉。已知巧克力有c种颜色,问拿n次巧克力后,桌上有m颗巧克力的概率,输出结果精确到小数点后三位。
思路:整体思路是dp。dp[i][j]表示拿了i次之后桌子上剩下j个巧克力的概率。这道题的关键在于:N, M <= 1000000,而要求保留三位小数,所以n较大的情况可以省略计算。discuss里有人给出了证明(唉,概率学的不好,看也是懵懵懂懂):
这就是个典型的markov链:q(t+1)=P*q(t) -> q(n)=(P^n)*q(0)=Q'*D^n*Q*q(0)
P包含1、-1以及其他绝对值小于1的特征值,所以D的偶次方和奇次方会分别收敛到不同对角阵
所以n要区分奇偶。n的上界可以这么估计:
c=100的时候,可以得到绝对值小于1且最大的特征值0.98,那么由0.98^n<0.001可得n>342
#include <stdio.h>
#include <string.h>
#define N 1005
double dp[N][N];
int num,n,m;
int main(){
freopen("a.txt","r",stdin);
while(scanf("%d",&num) && num){
int i,j;
scanf("%d %d",&n,&m);
memset(dp,0,sizeof(dp));
if(m>num || m>n || ((m+n)%2==1)){
printf("0.000\n");
continue;
}
if(n>1001)//按照discuss那位仁兄的意思,应该大于342的就不用算了
n = (n%2)?1001:1000;
dp[0][0] = dp[1][1] = 1;//dp[0][0]不可少,因为输入可能(n,m)=(0,0)
for(i = 2;i<=n;i++){
for(j = 0;j<=num;j++){
if(j!=0)
dp[i][j] += dp[i-1][j-1]*(num-j+1)/num;
if(j!=num)
dp[i][j] += dp[i-1][j+1]*(j+1)/num;
}
}
printf("%.3lf\n",dp[n][m]);
}
return 0;
}