zoukankan      html  css  js  c++  java
  • ACM数论之旅14---抽屉原理,鸽巢原理,球盒原理(叫法不一又有什么关系呢╮(╯▽╰)╭)

    这章没有什么算法可言,单纯的你懂了原理后会不会运用(反正我基本没怎么用过 ̄ 3 ̄)

    有366人,那么至少有两人同一天出生(好孩子就不要在意闰年啦( ̄▽ ̄"))

    有13人,那么至少有两人同一月出生

    这就是抽屉原理

    抽屉原理:把n+1个物品放到n个抽屉里,那么至少有两个物品在同一个抽屉里

    鸽巢原理:把n+1个鸽子放到n个鸽巢里,那么至少有两个鸽子在同一个鸽巢里

    球盒原理:把n+1个小球放到n个球盒里,那么至少有两个小球在同一个球盒里

    (你看,我都帮你们解释里一遍(≧︶≦*))

    其实抽屉原理有两个

    第一抽屉原理

    原理1: 把多于n+k个的物体放到n个抽屉里,则至少有一个抽屉里的东西不少于两件。

    原理2 :把多于mn(m乘以n)+1(n不为0)个的物体放到n个抽屉里,则至少有一个抽屉里有不少于(m+1)的物体。

    原理3 :把无穷多件物体放入n个抽屉,则至少有一个抽屉里 有无穷个物体。
     
    原理1 、2 、3都是第一抽屉原理的表述。

    第二抽屉原理

    把(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 }
    View Code

    其实这题的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 }
    View Code

    这个原理懂了,一定要学会用,要不然碰上别的题目一样不会(又在黑自己。。。( ̄▽ ̄"))

  • 相关阅读:
    1.20
    1.18
    4.16python安装
    4.15Android学习
    4.14Android学习
    4.13Android学习
    4.12Android学习
    4.11Android学习
    4.10Android学习
    4.09Android学习
  • 原文地址:https://www.cnblogs.com/linyujun/p/5210466.html
Copyright © 2011-2022 走看看