朋友的礼物-c#求解-英雄会在线编程题目

来源:互联网 发布:mac air 装win8 编辑:程序博客网 时间:2024/06/10 14:55

      周末闲来没事,就把csdn的这道朋友的礼物这道题做了,其实这道题就是一个排列组合,然后求概率的问题。先看题目吧。

朋友的礼物
  • 北京
  • 难 度 等 级:
  •    
题目详情

n个人,每个人都有一件礼物想送给他人,他们决定把礼物混在一起,然后每个人随机拿走一件,问恰好有m个人拿到的礼物恰好是自己的概率是多少?

输出结果四舍五入,保留8位小数,为了保证精度,我们用字符串作为返回类型。

输入:n,m (0<n<100, 0<=m<=n)


例如:

n = 2,m = 1,输出:0.00000000;

n = 99,m = 0,输出:0.36787944

 

 

解题思路,其实这道题已经把解题思路给了出来,举例中,假如99个都是拿到不是自己的礼物,这点提示了我。

先把这道题转换成排列组合,就1~n个数,把它们全部取出来,取出来的结果是:第k次取出来的数恰好是k,而正好有m个这样的概率。

根据提示,我们先考虑这种情况,给你n个数,取出来的结果是,没有一个数对应自己,也就是取第一个数不会取到1,取第二个数不会取到2,。。。。。这样的概率。

其次,我们还要考虑这种情况,从n个数中,取出m个,对应的都是自己。

 

根据上述分析,这道题就被拆分成两部分了

首先考虑从n个数中,取出m个,对应的都是自己。这个比较好计算出来

比如取第一个数,有n种可能,取到自己的概率是1/n,

第二个数有(n-1)种可能,取到自己的概率是1/(n-1),

..........................

第m个数有(n-m+1)种可能,取到自己的概率是1/(n-m+),

因此,概率就是p0=1/n*1/(n-1)*....................*1/(n-m+1);

 

注意:这里只是计算从n个取出m个相同,而且是连续的,也就是计算了一次,事实上还有不连续的,这有多少种可能了?这个就是从n个中取出m个数的组合的可能性,因此,还需要乘以次数,次数就是组合了C(n,m)

C(n,m)=n*(n-1)*(n-2)*.....*(n-m+1)/[m*(m-1)*...1]

 

这样就得到从n个中取出m个相同的概率p1=p0*p1,

这样就得到p1=1/m!;

 

现在我们再考虑第二部分,剩下的n-m个数,均不能是自己,因为前面拿到的都是自己的,所以,我们就可以考虑这样一个情况,从1~n个数中,拿到的均不是自己的数。

取第一个数,因为不能是自己,所有n-1种可取方式,概率就是(n-1)/n;

这里要考虑取第二个数:有两种情况

第一种情况,取到的是第一个数[1],概率为1/(n-1)

第二种情况,取到的是后面的数【3~n】,概率为(n-2)/(n-1)

 

当取第二个数为第一种情况时,我们发现剩下的n-2个数,和我们考虑的情况完全一样,相当于降次处理了,这个就是递归调用了。后面就很好处理了。

当取第二个数为第二种情况时,第二个数取到的就是第三个数,剩下的就是【1】和【3~n】,这时,我们发现,它自身开始出现相同的循环了。因此啊,我们就可以构造出第二递归调用函数了。

 

第一个函数:从n个数中取出一个数,其中,这n个数中包含自己

public static double cal(int n)
        {
            double result = 0;
            double p=0;
            if (n ==1)
                return 0;
            if(n==2)
                return 0.5;
            p = (double)(n - 1) / n;
            result = p * calNo(n - 1);
            return result;
        }

第二个函数,将从n个数中取出一个数,其中,这n个数中不包含自己

public static double calNo(int n)
        {
            double result = 0;
            if (n == 1)
                return 1;
            if (noDic.ContainsKey(n))
            {
                result = noDic[n];
            }
            else
            {
                result = (double)1 / n * cal(n - 1) + (double)(n - 1) / n * calNo(n - 1);
                if (!noDic.ContainsKey(n))
                {
                    noDic.Add(n, result);
                }
            }
            return result;
        }

为了缓存中间值,我们把中间值保存下来,减少计算次数。

第二步的函数两个,ok

 

下面把第一步的函数补全:

public static double  cal(int n, int m)
        {
            double result = 1;
            if (n == 1)
            {
                return 0;
            }
           
            for (int i = 0; i < m; i++)
            {
                result = (double)result /(m-i);
            }
            if (m == n)
            {
                m = n - 1;
            }
            else
            {
                result = result * cal(n - m);
            }
            return result;
        }

这里要考虑n==m,当这种情况出现时,就不用第二步的处理

 

到这里基本就结束了,根据题目要求,吧返回的double转换成string就可以了

 

整个做下来,3个函数,也可以整合成2个函数,而且步骤都不多,代码就比较少,提交,ok,通过没问题。

 

其实,从函数的递推关系可以看出,我们直接从最后一步往前推,那么就不需要递归调用了,直接一个for循环就解决了,这个也不是难事。

题目就做到这里吧。

 

1 0
原创粉丝点击