poj 2356 Find a multiple 【抽屉原理应用】【在n个数组成的数列中找出若干个连续数使它们之和能被n整除】

来源:互联网 发布:中学生预防网络诈骗 编辑:程序博客网 时间:2024/06/05 04:00
Find a multiple
Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 6595 Accepted: 2878 Special Judge

Description

The input contains N natural (i.e. positive integer) numbers ( N <= 10000 ). Each of that numbers is not greater than 15000. This numbers are not necessarily different (so it may happen that two or more of them will be equal). Your task is to choose a few of given numbers ( 1 <= few <= N ) so that the sum of chosen numbers is multiple for N (i.e. N * k = (sum of chosen numbers) for some natural number k).

Input

The first line of the input contains the single number N. Each of next N lines contains one number from the given set.

Output

In case your program decides that the target set of numbers can not be found it should print to the output the single number 0. Otherwise it should print the number of the chosen numbers in the first line followed by the chosen numbers themselves (on a separate line each) in arbitrary order.

If there are more than one set of numbers with required properties you should print to the output only one (preferably your favorite) of them.

Sample Input

512341

Sample Output

223
抽屉原理典型应用:一个由n个数组成的数列 一定能找出若干个连续的数使它们之和能被n整除。
解释:n个数记为a[1],a[2],...a[n].设置一个数组sum,其存储信息为sum[i] = a[1] + a[2] + ...a[i];  
情况一:存在一个k(1 <= k <= n),使得sum[k] % n == 0,那么就得证;
情况二:对于任意的k(1 <= k <= n),都有sum[k] % n != 0。       那么     对于n个sum数组的元素将会得到n个余数(即sum[k] % n且这些余数大于0且小于n)。这样对于n个余数(1 <= 余数 <= n-1),我们一定可以找到两个相等的余数,且它们对应sum数组中的start和end元素,使得(sum[end] - sum[start]%n==0。得证。
代码实现: (后面有简单点的思路)
#include <cstdio>#include <cstring>#define MAX 10000+10using namespace std;int a[MAX], sum[MAX], vis[MAX];int main(){int n, i, j;int start, end;//start记录连续数列第一个数的位置 end记录最后一个数的位置 int exist;//判断是否已经找到重复余数int mark;//记录重复的余数  while(scanf("%d", &n) != EOF){memset(vis, 0, sizeof(vis));memset(sum, 0, sizeof(sum));exist = 0; mark = 0; start = end = 0; for(i = 1; i <= n; i++){scanf("%d", &a[i]);if(exist)//已经找到 continue;sum[i] += a[i] + sum[i-1];if(sum[i]%n == 0)//直接能被n整除 {exist = 1;start = 1;    end = i; continue;}if(vis[sum[i]%n]) //余数已经出现过{exist = 1;mark = sum[i]%n;//记录重复余数 end = i;//记录结束位置 continue;} else vis[sum[i]%n] = 1;}if(start == 1)//起点从1开始的 {printf("%d\n", end);for(i = 1; i <= end; i++)printf("%d\n", a[i]);}else//起点不是从1开始的 {for(i = 1; i <= n; i++)    {    if(sum[i]%n == mark)//找到第一个出现重复余数的位置     {    start = i+1;//记录位置     break;    }    }    printf("%d\n", end-start+1);    for(i = start; i <= end; i++)    printf("%d\n", a[i]); }}return 0;}


 

还有一种更方便的思路:用vis[i]记录余数为i时的数字位置。当检测到a[i]时,余数为k, 若直接整除start = 1,end = i; 若余数k已出现过 start = vis[k]+1,end = i。这样就避免再次查找起点位置的步骤。  还有一点:可以用sum代替sum数组实现。

#include <cstdio>#include <cstring>#define LL long long#define MAX 10000+10using namespace std;LL sum;//防止超intint a[MAX], vis[MAX];int main(){int i, j, n;int exist, start, end;while(scanf("%d", &n) != EOF){sum = 0; exist = 0;memset(vis, 0, sizeof(vis));for(i = 1; i <= n; ++i){scanf("%d", &a[i]);if(exist)continue;sum += a[i];if(sum % n == 0){exist = 1;start = 1;end = i;continue;}if(!vis[sum%n])vis[sum%n] = i;else{exist = 1;start = vis[sum%n]+1;end = i;}}printf("%d\n", end-start+1);for(i = start; i <= end; i++)printf("%d\n", a[i]);}return 0;}  


0 0