首页 > 代码库 > POJ 2356 Find a multiple (dp + 鸽笼原理)

POJ 2356 Find a multiple (dp + 鸽笼原理)

OJ题目:click here~~

题目分析:n个数,从中取若干个数,和为n的倍数。给出一种取法。

因为只要给出其中一种方案就行,鸽笼原理可以求出取出的数为连续的方案。

关于鸽笼原理,点这里~

直接贴过来:

有n+1件或n+1件以上的物品要放到n个抽屉中,那么至少有一个抽屉里有两个或两个以上物品。


如果你知道这个结论:
a1,a2,a3...am是正整数序列,至少存在整数k和r,1<=k<r<=m,使得ak+a(k+1)+...+a(r)是m的倍数。

证明比较简单:

Sk表示前k个数之和,

 (1)若Sk%m==0,前k个数就是m的倍数

(2)如果Sn与St模m同余,那么从t+1到n这些数之和模m等于0.

即使你不知道这个结论,DP厉害的话,应该能想到用 前n项的和 去思考的思想

有这个结论知必有解。

贴代码之前,在总结一下鸽笼原理的结论:
推论1:m只鸽子,n个笼,则至少有一个鸽笼里有不少于[(m-1)/n]+1只鸽子。

推论2:若取n*(m-1)+1个球放进n个盒子,则至少有1个盒子有m个球。

推论3:若m1,m2,...mn是n个正整数,而且(m1+m2+...+mn)/n>r-1

则m1,m2,...mn中至少有一个数不小于r

AC_CODE

const int Max_N = 10002;
int   main(){
      int  n , a , i , j  , yes = 0 , x[Max_N] ,sum = 0 , visit[Max_N];
      memset(visit , -1 , sizeof(visit));
      while(cin >> n){
        for(i = 1;i <= n;i++) scanf("%d",&x[i]);
        for(i = 1;i <= n;i++){
            sum += x[i];
            sum %= n;
            if(sum == 0){
                printf("%d\n",i);
                for(j = 1;j <= i;j++)
                    printf("%d\n",x[j]);
                    yes = 1;
                    break;

            }
            else if(visit[sum] != -1){
                printf("%d\n",i - visit[sum]);
                for(j = visit[sum]+1;j <= i;j++)
                    printf("%d\n",x[j]);
                    yes = 1;
                    break;
            }
            else visit[sum] = i;


        }
        if(!yes) printf("0\n");
    }
      return 0 ;
}