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


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

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

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

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

  類似的模型個以舉出很多,比如366個人當中一定存在兩個人是同一天生日的。 它和上面最簡單的模型是一樣的,這里證明就不再累述了。


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

  顯然是n+1個人,答案顯而易見。


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

  我們先考慮所有的連續序列,有很多種情況,從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]上的整數,而如果等於零,顯然這里就符合題意了,所以我們在考慮沒有零的情況。

  這m個數對m取余,有m-1種情況,所以存在某兩個數,對m取余的結果是一樣的,假設為r,有以下等式。

  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個元素的序列a1、a2、a3、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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM