poj 2356 Find a multiple
抽屉原理典型应用:一个由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+10
using 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;
}