鸽巢原理简单应用

http://poj.org/problem?id=2356

从n个数里面取出一些数,这些数的和是n的倍数。并输出这些数。

先预处理出前n个数的和用sum[i]表示前i个数的和。若某个sum[i]是n的倍数,直接输出前i个数即可。

否则说明n个数中对n取余的结果有n-1种,即余数为(1~n-1),根据鸽巢原理知必定至少存在两个sum[i]与sum[j]对n取余的结果相等。那么i+1 ~ j之间的数之和一定是n的倍数。

#include <stdio.h>
#include <iostream>
#include <map>
#include <set>
#include <list>
#include <stack>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#include <stdlib.h>
#include <algorithm>
#define LL long long
#define _LL __int64
#define eps 1e-12
#define PI acos(-1.0)

using namespace std;
const int maxn = 10010;

int main()
{
	int n;
	int a[maxn];
	int sum[maxn];
	int hash[maxn];

	while(~scanf("%d",&n))
	{
		for(int i = 1; i <= n; i++)
			scanf("%d",&a[i]);
		sum[0] = 0;
		int i,flag = 0;
		memset(hash,-1,sizeof(hash));
		for(i = 1; i <= n; i++)
		{
			sum[i] = (sum[i-1]+a[i])%n;
			if(sum[i] == 0)
			{
				flag = 1;
				break;
			}
		}
		if(flag == 1)
		{
			printf("%d\n",i);
			for(int j = 1; j <= i; j++)
				printf("%d\n",a[j]);
			continue;
		}
		//for(int i = 1; i <= n; i++)
			//printf("sumi : %d\n",sum[i]);

		int s,t;
		for(int i = 1; i <= n; i++)
		{
			if(hash[sum[i]] == -1)
				hash[sum[i]] = i;
			else
			{
				s = hash[sum[i]];
				t = i;
				break;
			}
		}
		printf("%d\n",t-s);
		for(int i = s+1; i <= t; i++)
		{
			printf("%d\n",a[i]);
		}
	}
	return 0;
}

http://poj.org/problem?id=3370

与上题类似。只不过是取若干个数的和是m的倍数。跑一遍循环刚好,边哈希sum[i],边判断sum[i]是否等于0,一旦为0就输出,否则直到找到两个相等的sum[i]和sum[j],输出i~j即可。

#include <stdio.h>
#include <iostream>
#include <map>
#include <set>
#include <list>
#include <stack>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#include <stdlib.h>
#include <algorithm>
#define LL long long
#define _LL __int64
#define eps 1e-12
#define PI acos(-1.0)

using namespace std;
const int maxn = 100010;

int n,m;
int a[maxn];
int sum[maxn];
int hash[maxn];
int flag;
int s,t;

int main()
{
    while(~scanf("%d %d",&m,&n))
    {
        if(m == 0 && n == 0) break;
        sum[0] = 0;
        flag = 0;
        memset(hash,-1,sizeof(hash));

        for(int i = 1; i <= n; i++)
        {
            scanf("%d",&a[i]);
            if(flag) continue;

            sum[i] = (sum[i-1]+a[i])%m;
            if(sum[i] == 0)
            {
                flag = 1;
                for(int j = 1; j <= i; j++)
				{
					printf("%d",j);
					if(j == i) printf("\n");
					else printf(" ");
				}
            }

			else
			{
				if(hash[sum[i]] == -1)
					hash[sum[i]] = i;
				else
				{
					for(int j = hash[sum[i]]+1; j <= i; j++)
					{
						printf("%d",j);
						if(j == i)
							printf("\n");
						else printf(" ");
					}
					flag = 1;
				}
			}
        }
    }
    return 0;
}

鸽巢原理简单应用,布布扣,bubuko.com

时间: 2024-11-09 02:03:02

鸽巢原理简单应用的相关文章

poj 2356 Find a multiple 鸽巢原理的简单应用

题目要求任选几个自然数,使得他们的和是n的倍数. 由鸽巢原理如果我们只选连续的数,一定能得到解. 首先预处理前缀和模n下的sum,如果发现sum[i]==sum[j] 那么(sum[j]-sum[i])%n一定为0,直接输出i+1~j就够了. 为什么一定会有解,因为sum从1~n有n个数,而模n下的数只有0~n-1,把n个数放入0~n-1个数里,怎么也会有重复,所以这种构造方法一定没问题. 其实可以O(n)实现,嫌麻烦,就二重循环无脑了. #include <iostream> #includ

浅谈鸽巢原理的证明和简单应用

一.鸽巢原理的证明 1.定义: 若有n个鸽巢和kn+1只鸽子,所有的鸽子都进入鸽巢,那么至少有一个巢中有k+1只鸽子(n,k≥0). 2.证明(反证法): 若每个鸽巢中的鸽子数都不大于k,则总鸽子数<=kn,与已知相悖.得证. 3.拉姆齐(Ramsey)定理的证明:6个人中,要么存在三个人彼此互相认识,要么存在三个人彼此都不认识: 证明:设六个人为六个点,认识或不认识用两种不同颜色的线段代表,因为两人只有一种关系,所以任意一点一定会引出连向其他5点的五个线段,根据鸽巢定理,有2种关系,有2*2+

Euler Project 1 -- 鸽巢原理

题目要求 1000 内可以被 3 或者 5 整除的数字之和 可以很简单地这么写: print sum( [ i for i in xrange( 1000 ) if i % 3 == 0 or i % 5 == 0 ] ) 但是对于10^10的数据,要运行很长时间,可以利用一点鸽巢原理 10^10中能被 3 整除的数据和加上能被 5 整除的数据和减去能被 15 整除的数据和即可,求和时利用高斯公式. target = 10 ** 10 def SumDivisibleBy( n ): p = t

鸽巢原理(抽屉原理)的详解

抽屉原理 百科名片 桌上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们会发现至少会有一个抽屉里面放两个苹果.这一现象就是我们所说的“抽屉原理”. 抽屉原理的一般含义为:“如果每个抽屉代表一个集合,每一个苹果就可以代表一个元素,假如有n+1或多于n+1个元素放到n个集合中去,其中必定至少有一个集合里有两个元素.” 抽屉原理有时也被称为鸽巢原理(“如果有五个鸽子笼,养鸽人养了6只鸽子,那么当鸽子飞回笼中后,至少有一个笼子中装有2只鸽子”).它是组合数学中一个重要的原理. 第一抽屉原理 原

程序设计中的组合数学——鸽巢原理

回想到高中的的组合学中,有这样的问题,12个班中有13个人参加IOI的名额(前提每班至少出一个人),那么这会有几种分法? 一个很简单的思路就是把这13个名额摊开,然后拿11个隔板插到这13个名额形成的12个空隙里,然后用组合数的公式即可计算.而鸽巢原理的简单形式就和这个模型有联系. 我们知道,如果把12只鸽子放到11个巢里面,显然有一个巢会出现两只鸽子,这显而易见,同时也是鸽巢原理的最简单的形式. 它的证明也和简单,如果我们假设11个巢穴里最多有1个鸽子,那么各自的总数最多有11个,这一12只鸽

HDU 1205 吃糖果 鸽巢原理

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1205 题目描述: N种糖果, 每种有M个, 问你能不能每天吃和前一天不同的糖果将这些糖果都吃完 解题思路: 很简单的鸽巢原理, 找出最多的糖果数, 如果剩下的能够将这最多的糖果数之间的空隙也就是maxnum-1填充满就可以了 代码: #include <iostream> #include <cstdio> #include <string> #include <v

鸽巢原理和容斥原理

鸽巢原理又名抽屉原理 一种简单的表述法为: 若有n个笼子和n+1只鸽子,所有的鸽子都被关在鸽笼里,那么至少有一个笼子有至少2只鸽子. 另一种为: 若有n个笼子和kn+1只鸽子,所有的鸽子都被关在鸽笼里,那么至少有一个笼子有至少k+1只鸽子. 例子: 盒子里有10只黑袜子.12只蓝袜子,你需要拿一对同色的出来.假设你总共只能拿一次,只要3只就可以拿到相同颜色的袜子,因为颜色只有两种(鸽巢只有两个),而三只袜子(三只鸽子),从而得到“拿3只袜子出来,就能保证有一双同色”的结论. 有n个人(至少2人)

[HDU1205]吃糖果 题解(鸽巢原理)

[HDU1205]吃糖果 Description -HOHO,终于从Speakless手上赢走了所有的糖果,是Gardon吃糖果时有个特殊的癖好,就是不喜欢将一样的糖果放在一起吃,喜欢先吃一种,下一次吃另一种,这样:可是Gardon不知道是否存在一种吃糖果的顺序使得他能把所有糖果都吃完?请你写个程序帮忙计算一下. -Input:第一行有一个整数T,接下来T组数据,每组数据占2行,第一行是一个整数N(0<N<=1000000),第二行是N个数,表示N种糖果的数目Mi(0<Mi<=10

抽屉原理(鸽巢原理)

转至:https://blog.csdn.net/sand8o8time/article/details/77009749 一.抽屉原理初介绍: 桌上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们会发现至少会有一个抽屉里面至少放两个苹果.这一现象就是我们所说的“抽屉原理”. 抽屉原理的一般含义为:“如果每个抽屉代表一个集合,每一个苹果就可以代表一个元素,假如有n+1个元素放到n个集合中去,其中必定有一个集合里至少有两个元素.” 抽屉原理有时也被称为鸽巢原理.它是组合数学中一个重要的原