lightoj 1284 Grid
概率题:lightoj1284
最经典的想法:计算各个点的概率,由于概率满足相加原理,所以求和即可
这个题的数据范围看上去是很大的,x,y,z都是100,k是10000
所以两种可能:
枚举坐标x,y,z,数据规模到100*100*100
枚举k,最大能到O(nlogn),也就是10000*log(10000)
才能够保证不超时
必须选择枚举坐标:原因是概率题,计算每个坐标的符合条件的可能性,再求和
那么思考:坐标(i,j,k)什么时候会被选中?
根据题意,必须i在(i1,i2)之间,j在(j1,j2)之间,k在(k1,k2)之间,该点才会被选中
这是k=1的情况
如果k=3可以吗?k=5可以吗?所以,奇数次方都是满足条件的
那么,对于给定的坐标,k=1时,取的概率是可以求的
根据乘法原理:点在区间内=横坐标在x的区间内*纵坐标在y的区间内*竖坐标在z的区间内
在区间内的计算方法=所有情况-不在区间内的情况(也就是两侧)
所以有了calc函数求得p
然后,如何求得各点的概率?
设F(n)表示n次中改变了奇数次
设G(n)表示n次中改变了偶数次
那么,G(n)=1-F(n)且F(n)=F(n-1)*(1-p)+G(n-1)*p
可以解得F(n)=F(n-1)*(1-2p)+p,初始值为F(0)=0,F(1)=p
高中数学知识:左右两边同时除以(1-2p)^n,得到一个可以迭代的式子,根据边界条件,计算得:F(n)=0.5-0.5*(1-2p)^n
想通了,其实代码很简单的:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <math.h>
#include <cmath>
using namespace std;
int t,n,x,y,z;
double p,ans;
double calc(int a,int b){
return 1.0*b*b-1.0*(b-a)*(b-a)-(1.0)*(a-1)*(a-1);
}
int main(){
//freopen("input.txt","r",stdin);
scanf("%d",&t);
for(int Case=1;Case<=t;Case++){
ans=0;
scanf("%d%d%d%d",&x,&y,&z,&n);
for(int i=1;i<=x;i++)
for(int j=1;j<=y;j++)
for(int k=1;k<=z;k++){
//概率等于符合条件的情况除以所有选择情况
double p=calc(i,x)*calc(j,y)*calc(k,z)/x/x/y/y/z/z;
ans+=0.5-0.5*pow(1-2*p,1.0*n);
}
printf("Case %d: %.8lf\n",Case,ans);
}
return 0;
}