URAL1776-------用DP来求概率

题目地址: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的代码共享。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值