【2015暑假】鸽巢原理总结 【算法思路+组合数学】
组合数学之鸽巢原理
如果要把n+1物体放进n个盒子里,那么至少会有一个盒子包含2个或2个以上的物体。
现在问题来了:给你n个数,从中选出若干个数使得它们的和为n的倍数。
鸽巢原理表示:一定存在若干个连续的数,它们的和是n的倍数。
有的情况下,不光要知道一定存在,而且还要找出是哪些数,算法如下:
n = 4;
a[]:3 2 1 9
s[]:3 5 6 15 (注:)
先检查s[]中有没有某个s[i]%n==0,如果存在,那么这些数字它们加起来就可以整除n。如果不存在,那么由基本定理可知:必然会存在两个不同的和它们对n取余的余数相同,故部分和-= + +…+ 是n的倍数。
对于上面的例子一次求出s[]对n的余数:
yu[]:3 1 2 3
可知:和对4取余的余数均为3,故可以得到:2 + 1 + 9=12的和可以整除n。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <algorithm> 5 #define N 100000 6 7 using namespace std; 8 9 10 int main() 11 { 12 int n; 13 int a[N]; 14 int s[N]; 15 int yu[N]; 16 17 while(scanf("%d", &n)!=EOF) 18 { 19 for(int i=0; i<n; i++){ 20 scanf("%d", &a[i]); 21 } 22 23 bool flag=false; 24 int left=0, right; 25 s[0]=a[0]; 26 for(int i=1; i<n; i++){ 27 s[i]=s[i-1]+a[i]; 28 if(s[i]%n == 0){ 29 flag=true; 30 right=i; break; 31 } 32 } 33 if(flag){ 34 printf("YES\n"); 35 for(int i=0; i<=right; i++) 36 { 37 printf("%d ", a[i]); 38 } 39 }else{ 40 for(int i=0; i<n; i++){ 41 yu[i]=s[i]%n; 42 } 43 //查找yu[]里面哪两个数相等 44 for(int i=0; i<n; i++) 45 { 46 left=i; 47 for(int j=i+1; j<n; j++){ 48 if(yu[i]==yu[j]){ 49 right=j; break; 50 } 51 } 52 } 53 printf("YES\n"); 54 for(int i=left+1; i<=right; i++) 55 { 56 printf("%d ", a[i]); 57 } 58 } 59 } 60 return 0; 61 }