組合數學及其應用——鴿巢原理

  回想到高中的的組合學中,有這樣的問題,12個班中有13我的參加IOI的名額(前提每班至少出一我的),那麼這會有幾種分法?編程

  一個很簡單的思路就是把這13個名額攤開,而後拿11個隔板插到這13個名額造成的12個空隙裏,而後用組合數的公式便可計算。而鴿巢原理的簡單形式就和這個模型有聯繫。      數組

  咱們知道,若是把12只鴿子放到11個巢裏面,顯然有一個巢會出現兩隻鴿子,這顯而易見,同時也是鴿巢原理的最簡單的形式。app

  它的證實也和簡單,若是咱們假設11個巢穴裏最多有1個鴿子,那麼各自的總數最多有11個,這一12只鴿子的已知條件是不吻合的,因此假設是錯誤的,必定會有一個巢穴會出現2個鴿子。
  這裏須要補充的一點的是,因爲不一樣的人舉出不一樣的模型來引出原理,所以原理的名字可能有所不一樣,鴿巢原理還能夠叫作抽屜原理,鞋盒原理。
  基於對鴿巢原理最簡單模型的理解,咱們再舉出幾個引用來稍微更進一步的理解這個原理的應用。
  應用1:在13我的當中,必定存在2我的,他們的生日在同一個月。less

  相似的模型個以舉出不少,好比366我的當中必定存在兩我的是同一天生日的。 它和上面最簡單的模型是同樣的,這裏證實就再也不累述了。ide


  應用2:假設有n對夫婦,那麼最少挑出多少人,必定能保證挑出一對夫婦呢?spa

  顯然是n+1我的,答案顯而易見。3d


  應用3:給定m個整數a1,a2,a3……am,存在某個連續的序列(a1,a2,a3叫作連續序列,a1,a3,a4不是連續序列),這個序列的各個元素的和可以整除m。code

  咱們先考慮全部的連續序列,有不少種狀況,從a1開始組合,總數有m + (m - 1) + (m + 2)……+1,可是咱們這裏只需考慮從a1開始組合的連續序列。也就是考慮以下m個數。   a1  a1+a2  a1+a2+a3  a1+a2+a3+a4  …… (a1+a2+a3……+am)   咱們知道,任何一個數對m取餘,可能的結果只能是[0,m-1]上的整數,而若是等於零,顯然這裏就符合題意了,因此咱們在考慮沒有零的狀況。blog

  這m個數對m取餘,有m-1種狀況,因此存在某兩個數,對m取餘的結果是同樣的,假設爲r,有如下等式。ip

  a1+a2……+ak = pm + r   a1+a2……+al =  qm + r   將這兩個式子作減法,便可得證。


  應用4:一位國際象棋大師有11周來準備錦標賽,他計劃天天至少下1局棋,可是考慮到疲勞度的問題,他一個周不會下超過12局棋。證實存在連續的若干天,這期間象棋大師剛好下了21局棋盤。

   根據題意咱們不難給出表示大師第i天下棋的總局數的一個序列——a1,a2,a3……a77,而且根據題意可知他是嚴格的遞增的。所以咱們獲得下面的不等式。

  0<a1<a2<a3……<a77<132                       序列1   

  同時咱們在構造另一個序列 a1+21,a2+21,a3+21……a77+21 ,將會獲得下面的不等式。

  21<a1+21<a2+21……<a77+21<154          序列2   

  如今來看a1 a2 a3 ……a77  a1+21 a2+21 a3+21 ……a77+21這154個數,他們的取值範圍是[1,153],咱們如今要任意從中取2個數字——ai,aj。那麼是存在ai = aj的狀況的,而根據已知的序列(a1 a2 a3 ……a77  )是嚴格單調,所以不可能在序列1或序列2同時取出ai,aj,所以咱們必定是從序列1取出一個數,在序列2中取出一個數,因此有ai = aj = ak + 21,此時得證。

 

  應用5:從1,2,3……200中顯出101個整數。證實:在所選的這些整數之間存在兩個這樣的整數。其中一個可被另外一個整除。

  咱們從另外一個角度來考慮這些數字,任何一個數字能夠表示成2^k * a,其中k>=0 , a是奇數,對於[1,200],中的整數,a的全部狀況有1,3,5,……199這100種狀況,而咱們要抽取101個數字,那麼顯然,會有兩個數表示成以下的形式。 2^r * a , 2^s * a 而r  != s ,此時命題得證。


  應用6:設m和n是互素的正整數,並設a和b爲整數,其中0<=a<=m-1 ,0<= b<=n-1。因而存在正整數x,使得x除以m的餘數爲a,而且x除以n的餘數爲b;也就是說存在一個x = pm + a, 同時x = qm + b。   爲了證實咱們考慮以下n個數  a  a + m   a+ 2m   a+3m ……a+(n-1)m這n個數。

  咱們假設,這n個數裏沒有整除n的數(若是有的話,就符合題意了)。那麼如今有n個數,而餘數卻有n-1種狀況,根據鴿巢原理,有以下式子成立。   n = pm + a = qn + r   ①    n = im  + a = jn  + r   ② 倆式相減,咱們獲得(p-i)m = (q - j)n,因爲n與m互素,因此n應該是p-i的因子,而p≤n-1,顯然n不多是p-i的因子,假設是不成立的。因此在這n個數中,必需要出現一個對n取餘等於零的數,此時命題得證。


  

  下面給出鴿巢原理更加通常的形式:

                        

   鴿巢原理常被用於一些最大最小值的問題當中

    Ex1:

    一個果籃裝有蘋果、香蕉和句子。爲了保證籃子中至少有8個蘋果或者至少有6個香蕉或者至少有9個橘子,則放入籃子中的水果的最小件是多少?

    分析:這個問題實際上反向利用了通常形式的鴿巢原理,即通常形式的鴿巢原理具備充要性,換言之,定理1能夠以下等價的表述形式:

 

    即這個問題的答案是8+6+9-3+1 = 21

 

    先來看一個簡單的鴿巢原理的應用。

 

  Problem Description
  Every year there is the same problem at Halloween: Each neighbour is only willing to give a certain total number of sweets on that day, no matter how many children call on him, so it may happen that a child will get nothing if it is too late. To avoid conflicts, the children have decided they will put all sweets together and then divide them evenly among themselves. From last year's experience of Halloween they know how many sweets they get from each neighbour. Since they care more about justice than about the number of sweets they get, they want to select a subset of the neighbours to visit, so that in sharing every child receives the same number of sweets. They will not be satisfied if they have any sweets left which cannot be divided.
Your job is to help the children and present a solution.


  題目大意:給你一個含有n個元素的序列a一、a二、a三、a4……an和一個整數c,讓你找一個連續的區間段(a1,a2,a3是一個連續的區間段,a1,a3,a5則不是),使得這個區間段的全部元素的和能夠整除c。   數理分析:爲了解決這個問題咱們考慮下面n個數字 a1  a1+a2  a1+a2+a3  a1+a2+a3+a4  a1+a2+a3+a4+a5   a1+a2+a3+a4+a5……an。

  題幹中明顯給出c≤n,那咱們就假設c=n吧。顯然任意一個整數對c求餘會獲得[0,c-1]上的整數,下面咱們分兩種狀況來分析。

  ⑴若是這n個數字中對c取餘得0,那麼顯然知足了條件。

  ⑵若是這n個數字對c取餘沒有得0的狀況,那麼這n個數字中,要出現n-1種取餘結果,那麼顯然,一定存在某兩個數字對c取餘的餘數是相同的,咱們便會獲得下面的等式。(這裏假設k > l,想想,爲何不會相等)   

  a1+a2+a3+……ak = p*c + r  ①   

  a1+a2+a3+……al = q*c  + r  ②   兩式相減咱們會獲得,a(l+1) + a(l+2)……+a(k) = (p - q)*c。 顯然此時咱們找到了咱們想要的區間。
  編程實現:基於上面的數理分析,再編程實現上,咱們須要獲得上面討論的這n個數,而後把取模的結果做爲下標,數值做爲題設給定序列的下標,在構造一個記錄數組Mod,這題就能夠迎刃而解。
  ac代碼以下。  

 

#include<stdio.h>
#include<string.h>

int main()
{
   int Sum[100005] , Mod[100005] , A[100005];
   int c , n;
   int i;
   int right , left;
     while(~scanf("%d%d",&c , &n) && (c || n))
       {
            memset(Sum , 0 , sizeof(Sum));
            memset(Mod , -1 , sizeof(Mod));
            Mod[0] = 0;
            for(i = 1;i <= n;i++)
              {
                 scanf("%d",&A[i]);
                 Sum[i] = (Sum[i - 1] + A[i]) % c;

                 if(Mod[Sum[i]] == -1)

                        Mod[Sum[i]] = i;

                   else
                     {
                        left  = Mod[Sum[i]];
                        right = i;
                     }
              }
               for(i = left + 1;i <= right;i++)
               {
                 if(i == right)
                    printf("%d\n",i);

                 else
                    printf("%d ",i);
               }


       }
}

 


  來看一道很是簡單的鴿巢原理的題目。(Problem source : 1205)

  

Problem Description
HOHO,終於從Speakless手上贏走了全部的糖果,是Gardon吃糖果時有個特殊的癖好,就是不喜歡將同樣的糖果放在一塊兒吃,喜歡先吃一種,下一次吃另外一種,這樣;但是Gardon不知道是否存在一種吃糖果的順序使得他能把全部糖果都吃完?請你寫個程序幫忙計算一下。


  數理分析:這裏就是一個上文中咱們引出鴿巢原理舉出的例子。咱們從糖果數最多的「某種糖果」開始分析(由於若是數目不是最多的,是很是容易構造出不間隔的序列,而數目更多的某種糖果是不是不間隔的你是不得而知的)。

  咱們假設最多的糖果數目是maxn,咱們將這maxn個糖果排成一列,顯然構造出了maxn + 1個間隔,而咱們只須要找到其餘種類的糖果填掉中間的maxn - 1個間隔,就能夠構造出咱們所須要的序列,這裏就是體現了鴿巢原理的地方。

  編程實現:數理上的分析是很是簡單的,但實際編程如何進行比較呢?在輸入各類糖果並找出最大糖果數是十分好操做的,找到了最大的糖果數maxn,再如何進行比較呢?   這裏咱們想,咱們還須要的是maxn - 1個糖果數,有了這些糖果,咱們就能構造出所須要的序列,而這maxn - 1個糖果是不是同一種糖果是可有可無的一件事情,因此咱們會獲得一個判斷式:

    sum - maxn ≥ maxn - 1.

   可能有人會疑問,這maxn - 1個糖果的種類真的是可有可無的麼?若是某種糖果數大於了maxn,不就存在了不知足的狀況了麼?  針對第一個疑問,的確是可有可無,由於這maxn - 1 個糖果的做用是分隔那maxn - 1個糖果 ,放入「間隔」的同時其實本身也被「隔起來了」,因此糖果種類在maxn - 1箇中並不起做用。而針對第二個疑問,就更顯而易見來了,若是某種糖果大於maxn個,顯然這與咱們先前的假設最大糖果數是maxn是不符的,所以無需給予考。
  代碼以下。   

 

  #include<stdio.h>

int main()
{
      int n  , T , maxn , i , num;
      __int64 sum;

      scanf("%d",&T);
      while(T--)
        {
           scanf("%d",&n);
           maxn = 0 , sum = 0;
             while(n--)
               {
                  scanf("%d",&num);
                  if(num > maxn)
                        maxn  = num;
                  sum += num;
               }
           if(sum - maxn >= maxn - 1)  printf("Yes\n");
        else                        printf("No\n");
        }
}

 

參考系:《組合數學》 Richard A.Brualdi

相關文章
相關標籤/搜索