题目地址:http://acm.timus.ru/problem.aspx?space=1&num=1776
题目意思:
有n个火箭给你发射
每次发射间隔为10s
第一次,即第0s发射1,n
然后在发射其它的
每次只要处在已经发射火箭的中间的都可以发射,也就是可以同时发射多个
要你求期望
题目思路:
首先申明这份代码其实是参考的http://www.nocow.cn/index.php/%E9%A6%96%E9%A1%B5 ZZY的代码
我们用f[i][j]来表示发射i个火箭需要j个10s的概率
用s[i][j]来表示发射i个火箭需要<=j个10s的概率
即s[i][j] = sum(f[i][k]) 1<=k<=j
那么对于一个f[i][j]来说,我们怎么求呢
我们可以枚举1~i之间的某个点作为最后一个被发射的火箭
那么他的概率就是左边的j-1个以及右边的i-j个都被发射之后再发射他的的概率
不仅如此,再计算概率的时候,我们还要枚举发射发射火箭用了多久的时间
即可以枚举1到某个数t,最会的情况就是n
事实上,我们可以令l=j-1,r=i-j代表位于j左右两边已经发射的火箭个数,那么前面最多耗的时间也就是max(l,r)+1
为什么要加1呢?因为还有我们再j这个位置发射的时间
PS:我们用的都是单位时间,最后再*10就OK
我们令k表示本次花费的时间
得到如下的代码:
for(int i=1;i<maxn;i++)
{
for(int j=1;j<=i;j++)
{
int l=j-1;
int r=i-j;
int t=max(l,r)+1;
for(int k=1;k<=t;k++)
{
f[i][k] += (f[l][k-1]*s[r][k-1]+f[r][k-1]*s[l][k-1]-f[l][k-1]*f[r][k-1])/i;
/*
其实这个的核心就是这个
*/
}
}
//update s 数组
for(int j=1;j<maxn;j++)
s[i][j] = s[i][j-1]+f[i][j];
}
其中公式:
f[i][k] += (f[l][k-1]*s[r][k-1]+f[r][k-1]*s[l][k-1]-f[l][k-1]*f[r][k-1])/i;
的意思是发射左边的l个火箭用了k-1个乘以右边发射r个花费小于等于k-1单位时间的概率,
以及发射右边r个火箭花费了k-1个时间乘以左边发射l个花费小于等于k-1个单位时间的概率
那-f[l][k-1]*f[r][k-1]是什么呢?我们之前提到s[i][j] = sum(f[i][k]) 1<=k<=j
所以在上面进行:
f[i][k] += (f[l][k-1]*s[r][k-1]+f[r][k-1]*s[l][k-1]-f[l][k-1]*f[r][k-1])/i;的时候我们实际上多计算了一个f[l][k-1]*f[r][k-1]的
所以要减去,至于后面的/i就是本次的概率
然后再用
for(int j=1;j<maxn;j++)
s[i][j] = s[i][j-1]+f[i][j];
来更新s[i][j]
在本题中我还使用了打表的方法,稍微提高了程序的效率
下面上代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 401;
double f[maxn][maxn];
double s[maxn][maxn];
int main()
{
memset(f,0,sizeof(f));
memset(s,0,sizeof(s));
f[0][0]=1.0;
for(int i=0;i<maxn;i++)
s[0][i]=1.0;
for(int i=1;i<maxn;i++)
{
for(int j=1;j<=i;j++)
{
int l=j-1;
int r=i-j;
int t=max(l,r)+1;
for(int k=1;k<=t;k++)
{
f[i][k] += (f[l][k-1]*s[r][k-1]+f[r][k-1]*s[l][k-1]-f[l][k-1]*f[r][k-1])/i;
/*
其实这个的核心就是这个
*/
}
}
//update s 数组
for(int j=1;j<maxn;j++)
s[i][j] = s[i][j-1]+f[i][j];
}
int n;
while(~scanf("%d",&n))
{
n=n-2;
double ans=0;
for(int i=1;i<=n;i++)
ans+=f[n][i]*i*10;
printf("%.10lf\n",ans);
}
return 0;
}
感谢ZZY的代码共享。