回想到高中的的组合学中,有这样的问题,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