回想到高中的的組合學中,有這樣的問題,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
先來看一個簡單的鴿巢原理的應用。
題目大意:給你一個含有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)
數理分析:這裏就是一個上文中咱們引出鴿巢原理舉出的例子。咱們從糖果數最多的「某種糖果」開始分析(由於若是數目不是最多的,是很是容易構造出不間隔的序列,而數目更多的某種糖果是不是不間隔的你是不得而知的)。
咱們假設最多的糖果數目是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