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

博客文章介绍了如何运用抽屉原理来解决POJ 2356问题,即在给定的数列中找到若干连续数,使得它们的和能被数列长度n整除。文章提出了利用vis数组记录余数位置的方法,通过优化起始查找点避免重复搜索,同时指出可以用sum变量替代sum数组以简化实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Find a multiple
Time Limit: 1000MS Memory Limit: 65536K
Total 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

5
1
2
3
4
1

Sample Output

2
2
3
抽屉原理典型应用:一个由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;
}


 

还有一种更方便的思路:用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+10
using namespace std;
LL sum;//防止超int
int 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;
} 
 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值