汤氏思考题:http://blog.renren.com/blog/348686342/802082372
原文摘录:汤氏动脑题:假设一个教室里有300个座位,有300个同学且每人拥有一个固定的座位,我们三个是最早进教室的并且随便坐在了别人的座位上,那么这时这个座位的同学来了之后也只能坐在其他人的座位上,进而影响更多的人。请从概率论的角度分析,仅由最初我三个人坐在别人位置上而导致300人到齐后坐在别人位置上的总人数。
假设当前有m个人进来,其中x个人没有坐在自己的位置上,其中n个人自己的座位是空的(显然这n个人只能是我们三个中的,故n<=3),这个状态用三维坐标(m,x,n)表示
那么现在再进来1个人后,
- 假若他的位置被占(概率是n/(300-m)),
- 若他走向那n个(其实就是我们3个其中的一个)人的座位(概率n/(300-m)),现在的状态变为(m+1,x+1,n-1)
- 若他走向那300-m-n个人的座位(概率为(300-m-n)/(300-m)),现在的状态变为(m+1,x+1,n)
- 假若他的位置没有被占(概率(300-m-n)/(300-m)),显然他将走向自己的位置,现在的状态变为(m+1,x,n)
- (3,3,0)仅仅是我们3个人互相换了位置,有2种情况
- (3,3,1)我们3个人只有一个人的位置是空,有297×3×3种情况
- (3,3,2)我们3个人只有一个人的位置坐了人,有297×296×3×2种情况
- (3,3,3)我们3个人座位都空着,有297×296×295种情况
okay,到现在,动归转移方程写好了,初始状态也搞定了,程序显而易见了,复杂度呢,时间和空间复杂度都是O(300×300×3),更一般的,如果是一共N个同学的,初始我们有M个人没坐自己座位,复杂度是O(MN^2)
代码如下:
#include <stdio.h>
#define N 300 //一共N个座位
double f[N+10][N+10][4] = {0};
int main()
{
//四种初始情况的个数
double A = 2;
double B = (N-3) * 3 * 3;
double C = (N-3) * (N-2) * 3 * 2;
double D = (N-3) * (N-2) * (N-1);
double E = A + B + C + D;
double ans = 0;
int m,x,n;
//四种初始情况的概率
f[3][3][0] = A / E;
f[3][3][1] = B / E;
f[3][3][2] = C / E;
f[3][3][3] = D / E;
//动归求解
for(m = 3 ; m < N ; m++)
for(x = 3 ; x <= m ; x++)
for(n = 0 ; n <= 3 ; n++)
{
if(n)f[m+1][x+1][n-1] += f[m][x][n] * n * n/((N-m)*(N-m));
f[m+1][x+1][n] += f[m][x][n] * n * (N-m-n)/((N-m)*(N-m));
f[m+1][x][n] += f[m][x][n] * (N-m-n) / (N-m);
}
//ans即为最后的期望,f[N][0][0]是为了验证最后是否概率和为1
for(n = 3 ; n <= N ; n++)
{
ans += n*f[N][n][0];
f[N][0][0] += f[N][n][0];
}
printf("%lf %lf\n",ans,f[N][0][0]);
return 0;
}
import java.util.Date;
public class Tangshi {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Date start = new Date();
int N = 300;
double f[][][] = new double[N + 10][N + 10][4];
// 四种初始情况的个数
double A = 2;
double B = (N - 3) * 3 * 3;
double C = (N - 3) * (N - 2) * 3 * 2;
double D = (N - 3) * (N - 2) * (N - 1);
double E = A + B + C + D;
double ans = 0;
int m, x, n;
// 四种初始情况的概率
f[3][3][0] = A / E;
f[3][3][1] = B / E;
f[3][3][2] = C / E;
f[3][3][3] = D / E;
// 动归求解
for (m = 3; m < N; m++)
for (x = 3; x <= m; x++)
for (n = 0; n <= 3; n++) {
if (n > 0)f[m + 1][x + 1][n - 1] += f[m][x][n] * n * n / ((N - m) * (N - m));
f[m + 1][x + 1][n] += f[m][x][n] * n * (N - m - n) / ((N - m) * (N - m));
f[m + 1][x][n] += f[m][x][n] * (N - m - n) / (N - m);
}
// ans即为最后的期望,f[N][0][0]是为了验证最后是否概率和为1
for (n = 3; n <= N; n++) {
ans += n * f[N][n][0];
f[N][0][0] += f[N][n][0];
}
System.out.println(ans + " " + f[N][0][0]);
Date end = new Date();
System.out.println("Time Cost:" + (end.getTime() - start.getTime())
+ "ms");// 计时
}
}
java这个时间结果是31ms
最后的结果是16.387016,个人感觉有点小,似乎不太可靠。。。。