[kuangbin带你飞]专题二十一 概率&期望 部分题解

2018-03-08  18:02:20

A - A Dangerous Maze LightOJ - 1027 

题意: 小花的迷宫入口有n个门  走每个门的概率都是相同的  每个门有一个值x  x可正可负  正代表x 分钟后可以征服迷宫  负代表|x|分钟之后小花会回到原点  问征服迷宫时间的期望

题解:假设小花平均E分钟可以征服迷宫

           如果小花刚开始选择了一个值为负的门  那么此时的时间变成了E+|Xi|  概率为1/n  因为选择每个门的概率都相同  PS:相当于在原来可以出去的基础上浪费了|Xi|分钟

           如果小花刚开始选择了一个值为正的门 那么此时的时间变成了Xi 概率为1/n 因为选择每个门的概率都相同

           假设值为正的门有n0个  值为负的门有n1个  正的门的值的总和为T0  负的门的值的总和为|T1|

           则   E = T0/n + (E+|Xi|)/n*n1

                     = T0/n + |T1|/n + E/n*n1

                 n0/n*E = (T0+|T1|)/n

                 E = (T0 + |T1|) / n0

刚接触概率与期望  感谢http://blog.csdn.net/jinglinxiao/article/details/70226889

代码如下:

#include<stdio.h>
#include<iostream>
#include<algorithm>

using namespace std;

int t;
int n , a[110];
int num , tmp , sum;

int gcd(int a , int b)//最大公因数
{
    return b==0?a:gcd(b , a%b);
}

int main()
{
    scanf("%d" , &t);
    for(int cas=1; cas<=t; cas++)
    {
        scanf("%d" , &n);
        num = 0;
        sum = 0;
        for(int i=0; i<n; i++)
        {
            scanf("%d" , &tmp);
            sum += abs(tmp);
            if(tmp < 0) continue;
            num++;
        }
        if(num == 0)
        {
            printf("Case %d: inf\n" , cas);
            continue;
        }
        tmp = gcd(sum , num);
        printf("Case %d: %d/%d\n" , cas , sum/tmp , num/tmp);
    }


    return 0;
}

B - Discovering Gold  LightOJ - 1030

题意: 小花进入到了一个1*n的山洞  n个单元格标号为1~n  刚开始小花在1号点  你可以想象为在玩飞行棋  走之前需要先掷骰子  掷出来的数字是几就走几步  如果超过了山洞的长度就重新掷   每个单元格上都有一定数量 对的黄金  问能够得到黄金的期望

题解: 这道题我是先求出了到达每个点的概率  然后将每个单元格的概率与黄金数量相乘将所有单元格相加 得到结果  相当于是加权平均值  概率的求法就是1号点的概率为1  之后他能够到达的点(不超过n)的概率相同 为1/x*(前继的概率)  x为最多能掷出来的数字

代码如下:

#include<stdio.h>
#include<iostream>
#include<queue>
//#include<algorithm>
#include<string.h>

using namespace std;

queue<int>Q;
int t , n , a[110];
double b[110];

void init()
{
    while( !Q.empty() )
        Q.pop();
    memset(b , 0 , sizeof(b));
}

void input()
{
    for(int i=1; i<=n; i++)
    {
        scanf("%d" , &a[i]);
    }
}

void solve()
{
    b[1] = 1.00;
    for(int i=1; i<=n; i++)
    {
        int j = 1;
        for( ; j<=6; j++)
        {
            if(i+j>n)
                break;
        }
        j--;
        for(int k=1; k<=j; k++)
        {
            b[i+k] += 1.00*b[i]/j;
        }
    }
    double sum = 0.00;
    for(int i=1; i<=n; i++)
    {
        sum += a[i]*b[i];
    }
    printf("%.8lf\n" , sum);
}

int main()
{
    scanf("%d" , &t);
    for(int cas=1; cas<=t; cas++)
    {
        scanf("%d" , &n);
        init();
        input();
        printf("Case %d: " , cas);
        if(n == 1)
            printf("%d\n" , a[1]);
        else if(n == 2)
            printf("%d\n" , a[1]+a[n]);
        else
            solve();
    }

    return 0

 

2018-03-09  14:26:3

题意: 小花是个聪明的孩子  给一个数字n  将这个数字除以其中一个因子  得到新的n  一直除下去  直到得到1  问除的次数的期望

题解: 每个数字除的次数可以根据他所有的因子得到  包括1与本身  设d[]表示每个数字除至1的次数的期望(一下简称次数)  那么一个数字的次数等于因子的次数加1  因为需要先除一下到因子  这里的因子指所有因子  假设有c个  那么取到每个因子的概率均为1/c  那么通过这个因子到1的次数的期望为d[因子]/c  假设i1  i2  i3...ic-1  ic 分别代表数字i的c个因子  那么可以得到  d[i] = 1+(d[i1]+d[i2]+...+d[ic-1]+d[ic])/c  化简可得  d[i] = (d[i1]+d[i2]+...+d[ic-1])+c/(c-1)  上面是不包括本身的所有因子的次数的期望  数字为1e5 经过一次预处理  每次输入可以O(1)的输出

代码如下:

#include<stdio.h>
#include<iostream>
#include<math.h>

using namespace std;

int t , n;
double d[100010];
int num;
double sum;
double tmp;

void init()
{
    d[1] = 0.00;
    for(int i=2; i<=100000; i++)
    {
        num = 0;
        sum = 0.0;
        tmp = sqrt(i);
        num += 2;// 1与本身
//            sum += d[1];//d[1]=0 可以省略
        for(int j=2; j<tmp; j++)
        {
            if(i%j != 0) continue;
            num += 2;
            sum += d[j];
            sum += d[i/j];
        }
        if(tmp == (int)tmp)
        {
            num++;
            sum += d[(int)tmp];
        }
//            printf("%d...%.8lf\n" , num , sum);
        d[i] = (sum+num)/(num-1);
    }
}

int main()
{
    scanf("%d" , &t);
    init();
    for(int cas=1; cas<=t; cas++)
    {
        scanf("%d" , &n);
        printf("Case %d: %.8lf\n" , cas , d[n]);
    }

    return 0;
} 

D - Just another Robbery  LightOJ - 1079

2018-05-06 15:37:51

题意:小花闲的没事要去抢银行  她的队友拼命拦住她说:“蚂蚁竞走了十年了,你冷静一点!”    但是我行故我在  我执意要翘课去抢劫  内心戏怎么这么多  小花的危险值的上限是P  一个分数  有n个银行  每个银行有v[i]元钱  抢劫的时候被抓住的概率是p[i]  问在不被抓住的情况下 最多可以抢劫多少钱

题解:这道题看到之后就知道是一道裸的 0 1 背包的题   每个银行有抢或不抢两种情况  不加空间优化的 01 背包  dp[i][j]在背包中代表装了i件物品   占据了j的体积得到的价值  这道题中dp[i][j]代表的是  抢了i家银行 得到j元钱  被抓到的概率  最后求n个银行都处理完之后  满足概率要求的最大的j  其实这个题刚开始看不会写 因为刚开始会把得到的钱当做价值  概率当做体积  这样的话 真是不知道怎么写 因为概率是小数啊  01 背包没有教我怎么遍历啊  于是转变想法就好  空间压缩的改一下就好

代码如下:

#include<stdio.h>
#include<iostream>

using namespace std;

int t;
int n;
double P;
int v[110];
double p[110];
double dp[105][105*105];
int sum;

void init()
{
    sum = 0;
}

void input()
{
    for(int i=1; i<=n; i++)
    {
        scanf("%d%lf" , &v[i] , &p[i]);
        sum += v[i];
    }
}

void solve()
{
    /*
    dp[i][j] 记录的是抢了i个银行 得到了j元钱 被抓的概率
    */
    for(int i=1; i<=sum; i++)
        dp[0][i] = -1;  //抢了0个银行  得到j元钱是不可能的 j>0
    dp[0][0] = 0;       //抢了0个银行  得到0元钱 被抓的概率是0
    for(int i=1; i<=n; i++)
    {
        for(int j=0; j<=sum; j++)
        {
            /*
            因为dp初始值都为0  所以只有在i-1=0的时候才会出现小于-0.5 / <0的情况
            */
            if(j<v[i] || dp[i-1][j-v[i]]<=-1)  //第i个银行强不了(j<v[i])  或者虽然第i个银行可以抢 但是如果抢了他  他前面的九不成立了  也就是虽然可以建二楼 但是你一楼就垮了 所以二楼还是一楼
                dp[i][j] = dp[i-1][j];
            else if(dp[i-1][j]<0)
                dp[i][j] = dp[i-1][j-v[i]] + (1-dp[i-1][j-v[i]])*p[i]; //第i个银行可以抢 因为j>v[i] if里对应抢i-1个银行得不到j元钱的情况 此时第i个银行一定得抢
            else
                dp[i][j] = min(dp[i-1][j], dp[i-1][j-v[i]]+(1-dp[i-1][j-v[i]])*p[i]);   //前i-1次被抓的概率是dp[i-1][j-v[i]] 于是不被抓的概率就是1-dp[i-1][j-v[i]] 再乘上此时被抓的概率
        }
    }
}

int main()
{
    scanf("%d" , &t);
    for(int cas=1; cas<=t; cas++)
    {
        scanf("%lf%d" , &P , &n);
        init();
        input();
        solve();
       int ans = 0;
//       for(int j=0; j<=n; j++)
//       {
       for(int i=0; i<=sum; i++)
       {
           if(dp[n][i]>-1 && dp[n][i]<P)
            ans = i;
//                printf("%lf..." , dp[j][i]);
       }
//        printf("\n");
//       }
       printf("Case %d: %d\n" , cas , ans);
    }


    return 0;
}

  

 

E - Birthday Paradox  LightOJ - 1104

题意: 给出一年的天数  不一定是365天  你要邀请朋友到你家做客  问最少邀请多少人会使得存在两个人生日相同的概率大于0.5 

题解:本小花想开一个二维dp数组  dp[i][0] 表示请i个人  所有人生日都不同的概率  dp[i][1]代表请i个人 至少有两个人生日相同的概率  后来发现 dp[i][1]不就等于1-dp[i][0]么  于是直接求dp[i][0]  一个人的时候为1   因为不可能出现生日相同 之后每加一个人就是乘上(n-i+1)/n 

疑问点:惹不起的出题人系列  我考虑了一下 输入为1 和 2的边界情况 发现输出的是1  于是心想 一个人怎么会出现两个人的生日概率相同啊  于是暗自得意 这就是这个题的坑点吧  自信满满的在n==1||n==2的时候特判输出了2  哈哈  不知道为什么WA了  把自以为对的特判删掉就对了 谁来告诉我一下 一个人的时候怎么有两个人的生日相同的概率呢。。  恰逢今天山东省赛  F题暴力1e42能过  让我又想起了去年青岛的区域赛暴力  于是今天真的惹不起出题人了

代码如下:

#include<stdio.h>
#include<iostream>

using namespace std;

int t;
int n;
double dp[100005];

int main()
{
//    printf("%lf.....%lf...\n" , 1.00*364/(365*365) , (1.00+364*3.0)/(365*365*365));
    scanf("%d" , &t);
    for(int cas=1; cas<=t; cas++)
    {
        scanf("%d" , &n);
//        if(n <= 2)
//        {
//            printf("Case %d: 2\n" , cas);
//            continue;
//        }
        dp[1] = 1;
//        dp[1][1] = 0;
        int i;
        for(i=2; i<=100000; i++)
        {
            dp[i] = dp[i-1] * (1.00*(n-i+1)/n);
//            dp[i][1] = dp[i-1][1] + dp[i-1][0]*(1.00*(i-1)/n);
//            printf("%d.....%lf........\n" , i , 1-dp[i]);
            if((1-dp[i]) >= 0.5)
                break;
        }
        printf("Case %d: %d\n" , cas , i-1);

    }


    return 0;
}

 

G - Dice (III)  LightOJ - 1248

题意: 小花掷硬币  火星硬币有n个面  要求每个面都出现过时的最少投掷次数的期望

思路: dp[i]记录得到i个面投掷次数的期望  我们可以知道dp[1]=1  因为投掷一次只能得到一个面  得到一个面投掷一次就够了

状态转移方程可以参考A题   我要得到第i个面的时候 我有 i-1/n 的概率得到和之前一样的面  有  (n-(i-1))/n的概率在dp[i-1]的基础上得到第 i 个面 

于是  dp[i] = dp[i-1]* (n-(i-1))/n +1 + dp[i]* (i-1)/n  为什么要加1呢  因为我们求的是次数的期望  得到一个新的面自然要加1

移项化简得  dp[i] = dp[i-1]+n/(n-i+1) 

代码如下:

#include<stdio.h>
#include<iostream>

using namespace std;

int t , n;
double dp[100005];

int main()
{
    scanf("%d" , &t);
    for(int cas=1; cas<=t; cas++)
    {
        scanf("%d" , &n);
        dp[1] = 1.00;
        for(int i=2; i<=n; i++)
        {
            dp[i] = dp[i-1]+1.00*n/(n-(i-1));
        }
        printf("Case %d: %.15f\n" , cas , dp[n]);

    }



    return 0;
}

 

 

posted @ 2018-03-09 14:32  Flower_Z  阅读(327)  评论(0编辑  收藏  举报