朋友的禮物-c#求解-英雄會在線編程題目

      週末閒來沒事,就把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循環就解決了,這個也不是難事。

題目就做到這裏吧。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章