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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/AgoniAngel/article/details/51956623

基本原理: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;
            }
        }
    }
}


猜你喜欢

转载自blog.csdn.net/AgoniAngel/article/details/51956623
POJ
今日推荐