這章沒有什麼算法可言,單純的你懂了原理後會不會運用(反正我基本沒怎麼用過 ̄ 3 ̄)算法
有366人,那麼至少有兩人同一天出生(好孩子就不要在乎閏年啦( ̄▽ ̄"))數組
有13人,那麼至少有兩人同一月出生ide
這就是抽屜原理spa
抽屜原理:把n+1個物品放到n個抽屜裏,那麼至少有兩個物品在同一個抽屜裏code
鴿巢原理:把n+1個鴿子放到n個鴿巢裏,那麼至少有兩個鴿子在同一個鴿巢裏blog
球盒原理:把n+1個小球放到n個球盒裏,那麼至少有兩個小球在同一個球盒裏string
(你看,我都幫大家解釋裏一遍(≧︶≦*))it
其實抽屜原理有兩個io
原理1: 把多於n+k個的物體放到n個抽屜裏,則至少有一個抽屜裏的東西很多於兩件。event
原理2 :把多於mn(m乘以n)+1(n不爲0)個的物體放到n個抽屜裏,則至少有一個抽屜裏有很多於(m+1)的物體。
把(mn-1)個物體放入n個抽屜中,其中必有一個抽屜中至多有(m—1)個物體(例如,將3×5-1=14個物體放入5個抽屜中,則一定有一個抽屜中的物體數少於等於3-1=2)。
原理懂了,可是你會運用嗎?
來看這一題
cf 577B
http://codeforces.com/problemset/problem/577/B
Modulo Sum
給你一個序列a1,a2...an,再給你一個數字m
問你能不能從中選出幾個數,把他們相加,令這個和可以整除m
能就是輸出YES,不能就輸出NO
不知道你有木有思路(O ° ω ° O )
正常講確定是dp咯,加一點剪枝,勉強卡過了(由於CF上面都是單組數據,多組可能就超時了)
AC代碼:
1 #include<cstdio> 2 #include<cstring> 3 const int N = (int)1e6 + 5; 4 int n, m; 5 int a[N]; 6 bool dp[2][1000];//滾動數組 7 bool work(){ 8 dp[0][a[0]] = true; 9 for(int i = 1; i < n; i ++){ 10 memset(dp[i & 1], 0, sizeof(bool)*1000); 11 dp[i & 1][a[i]] = true; 12 for(int j = 0; j < m; j ++){ 13 if(dp[(i-1) & 1][j]){ 14 dp[i & 1][(j + a[i]) % m] = true; 15 dp[i & 1][j] = true; 16 } 17 } 18 if(dp[i & 1][0]) return true; 19 } 20 return dp[(n-1) & 1][0]; 21 } 22 int main(){ 23 scanf("%d%d", &n, &m); 24 for(int i = 0; i < n; i ++){ 25 scanf("%d", &a[i]); 26 a[i] %= m; 27 } 28 puts(work() ? "YES" : "NO"); 29 }
其實這題的n雖然範圍大,可是咱們能夠加一個判斷,n>m的話,必然輸出YES
爲何?根據抽屜原理唄
先求前綴和求餘m,
若是有m+1個數,那麼就會產生m+1個前綴和,求餘完m,就會有m+1個餘數
咱們知道求餘完m會產生0~m-1總共m個餘數
那麼根據抽屜原理,至少有兩個相同的餘數
那麼他們之間的數的和求餘m就確定是0,因此n>m的話,必然輸出YES
好比
取兩個下標i和j(i < j)
(a1+a2+...+ai) % m = k
(a1+a2+...+aj) % m = k
那麼(ai+...+aj) %m = 0
因此問題解決啦
AC代碼:
1 #include<cstdio> 2 #include<cstring> 3 const int N = (int)1e6 + 5; 4 int n, m; 5 int a[N]; 6 bool dp[2][1000];//滾動數組 7 bool work(){ 8 if(n > m) return true;//多加這一句,TLE的代碼說不定就能AC 9 dp[0][a[0]] = true; 10 for(int i = 1; i < n; i ++){ 11 memset(dp[i & 1], 0, sizeof(bool)*1000); 12 dp[i & 1][a[i]] = true; 13 for(int j = 0; j < m; j ++){ 14 if(dp[(i-1) & 1][j]){ 15 dp[i & 1][(j + a[i]) % m] = true; 16 dp[i & 1][j] = true; 17 } 18 } 19 if(dp[i & 1][0]) return true; 20 } 21 return dp[(n-1) & 1][0]; 22 } 23 int main(){ 24 scanf("%d%d", &n, &m); 25 for(int i = 0; i < n; i ++){ 26 scanf("%d", &a[i]); 27 a[i] %= m; 28 } 29 puts(work() ? "YES" : "NO"); 30 }
這個原理懂了,必定要學會用,要否則碰上別的題目同樣不會(又在黑本身。。。( ̄▽ ̄"))