抽屉原理简单应用 POJ 2356 POJ 3370

来源:互联网 发布:淘宝卖家网页制作 编辑:程序博客网 时间:2024/05/22 03:17

基本原理:k+1个物体放入k个盒子,一定至少有一个盒子有2个或更多的物体。

数学语言描述为:m(m>=1)个元素分成n个组,那么总有一个组至少含有元素个数为[ m/n ](向上取整)。

重要推论:设a1,a2,...,am是正整数的序列,则一定存在整数k和l,1<= k < l <=m,使得连续子序列和ak+a(k+1)+...+al是m的倍数。

证明:设Sk表示前k项和,

(1)若有一个Sk是m的倍数,则定理已得证;

(2)设在上面的序列中没有一个Si(1<=i<=m)是m的倍数,令ri=Si%m。

我们已知上面的所有项都非m的倍数,则ri(1<=i<=m)都不为0。

根据抽屉原理,这m个余数在[1,m-1]里取值至少存在一对rh,rk,满足rh=rk,即Sh=Sk mod m。

不妨设h>k,则 Sh-Sk=a(k+1)+a(k+2)+...+ah≡0 mod m,证毕。




POJ 2356

题意:给出n个数,从中取若干个数,使得这些数和为n的倍数。给出其中一种取法。因为只要给出其中一种方案就行,抽屉原理可以求出取出的数为连续的方案。

思路:直接应用推论。

#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>#include <cstring>#include <string>#include <vector>#include <cctype>#define mst(a,b) memset(a,b,sizeof(a))typedef long long LL;using namespace std;const int N = 10001;int a[N],sum[N],r[N];//r[i]表示余数为i的数的下标int main(){    //freopen("test.txt","r",stdin);    int n;    while(~scanf("%d",&n)){        for(int i=0;i<n;i++){            r[i]=-1;            scanf("%d",&a[i]);        }        if(a[0]%n==0){            printf("1\n%d\n",a[0]);            continue;        }        sum[0]=a[0];        r[sum[0]%n]=0;        for(int i=1;i<n;i++){            sum[i]=a[i]+sum[i-1];            if(sum[i]%n==0){                printf("%d\n",i+1);                for(int j=0; j<=i; j++)                    printf("%d\n",a[j]);                break;            }            if(r[sum[i]%n]==-1)                r[sum[i]%n]=i;            else {                printf("%d\n",i-r[sum[i]%n]);                for(int j=r[sum[i]%n]+1;j<=i;j++)                    printf("%d\n",a[j]);                break;            }        }    }}

POJ 3370

题意:与上一题不同之处在于取的一组数要整除的不是n,而是c,其中1<=c<=n。另外输出的是选出来的数的下标,不是数本身。

思路:根据推论的证明过程,我们完全可以进一步推出:对于任意1<=c<=n,都必然能找到一个连续子序列满足其和是c的倍数。

#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>#include <cstring>#include <string>#include <vector>#include <cctype>#define mst(a,b) memset(a,b,sizeof(a))typedef long long LL;using namespace std;const int N = 100001;int a[N],r[N];LL sum[N];int main(){    //freopen("test.txt","r",stdin);    int n,c;    while(scanf("%d%d",&c,&n)&&(n||c)){        for(int i=0;i<n;i++){            r[i]=-1;            scanf("%d",&a[i]);        }        if(a[0]%c==0){            printf("1\n");            continue;        }        sum[0]=a[0];        r[sum[0]%c]=0;        for(int i=1;i<n;i++){            sum[i]=a[i]+sum[i-1];            if(sum[i]%c==0){                printf("1");                for(int j=1; j<=i; j++)                    printf(" %d",j+1);                printf("\n");                break;            }            if(r[sum[i]%c]==-1)                r[sum[i]%c]=i;            else {                printf("%d",r[sum[i]%c]+1+1);                for(int j=r[sum[i]%c]+2;j<=i;j++)                    printf(" %d",j+1);                printf("\n");                break;            }        }    }}


0 0