鸽巢原理小结

转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents           by---cxlove

最基础的原理便是n+1的物体放到n个盒子里,至少有一个盒子放了两个物体。

poj 2356

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

有n个数,从中选出几个数的和是n的倍数。

不得不说数学是个神奇的东西,结论是任意的n个数,必然能找到连续的m个数之和是n的倍数。

接下来简单证明一下,组合数学书中,黑书都有介绍。Sk表示a1+a2+……ak,如果Sk是n的倍数,那就直接取Sk了,否则S1-Sn除n的余数分布在1---(n-1)这n-1个数中,运用鸽巢原理,必然有两个的余数相同,即(Si%n)=(Sj%n),即(Sj-Si)%n=0,证毕。

/*
ID:cxlove
PROB:POJ 2356
DATA:2012.4.6
HINT:鸽巢原理
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main(){
	int n,k;
	int a[10005]={0},b[10005];
	while(scanf("%d",&n)!=EOF){
		bool flag=false;
		memset(b,0,sizeof(b));
		for(int i=1;i<=n;i++){
			scanf("%d",&k);
			if(flag) continue;
			a[i]=a[i-1]+k;
			if(a[i]%n==0){
				printf("%d\n",i);
				for(int j=1;j<=i;j++)
					printf("%d\n",a[j]-a[j-1]);
				flag=true;
			}
			else if(b[a[i]%n]){
				printf("%d\n",i-b[a[i]%n]);
				for(int j=b[a[i]%n]+1;j<=i;j++)
					printf("%d\n",a[j]-a[j-1]);
				flag=true;
			}
			else
				b[a[i]%n]=i;
		}
	}
	return 0;
}

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

和上题差不多,从n个数中找出m个数的和是c的倍数,因为c<=n,运用上面的证明,则必然存在连续的k个数是c的倍数,注意下可能会溢出就行了。


/*
ID:cxlove
PROB:POJ 3370
DATA:2012.4.6
HINT:鸽巢原理
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
LL a[100005]={0},b[100005];
int main(){
	int n,k,c;
	while(scanf("%d%d",&c,&n)!=EOF&&n+c){
		bool flag=false;
		memset(b,0,sizeof(b));
		for(int i=1;i<=n;i++){
			scanf("%d",&k);
			if(flag) continue;
			a[i]=a[i-1]+k;
			if(a[i]%c==0){
				for(int j=1;j<i;j++)
					printf("%d ",j);
				printf("%d\n",i);
				flag=true;
			}
			else if(b[a[i]%c]){
				for(int j=b[a[i]%c]+1;j<i;j++)
					printf("%d ",j);
				printf("%d\n",i);
				flag=true;
			}
			else
				b[a[i]%c]=i;
		}
	}
	return 0;
}


POJ 3145 http://poj.org/problem?id=3145

以前做的一题,鸽巢原理的巧妙运用。

有两种操作

B X,将数学x(1-500000)放到集合中,

A Y ,输出集合中模Y最小的数。如果有多个,输出最后放入集合的元素。

考虑以下问题,在0-(Y-1)中模Y最小的数一定是0-(Y-1)中数字最小的数,那么依此类推(Y~2*Y-1)(2*Y~3*Y-1)……,线段树处理数字的插入工作,并且查找区间的最小数字。

/*
ID:cxlove
PROB:Harmony Forever 
DATA:2012.2.24
HINT:线段树,鸽巢原理
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 1<<30
#define MAX 500000
using namespace std;
struct line{
	int left,right,mid;
	int val;
}L[MAX*4];
int pos[MAX+5],cnt,val[MAX+5];
void bulid(int step,int l,int r){
	L[step].left=l;
	L[step].right=r;
	L[step].mid=(l+r)/2;
	L[step].val=inf;
	if(l==r)
		return;
	bulid(step<<1,l,(l+r)/2);
	bulid(step<<1|1,(l+r)/2+1,r);
}
void update(int step,int pos){
	if(L[step].left>pos||L[step].right<pos)
		return;
	if(L[step].left>=pos&&L[step].right<=pos){
		L[step].val=pos;
		return ;
	}
	if(pos<=L[step].mid)
		update(step<<1,pos);
	else
		update(step<<1|1,pos);
	L[step].val=min(L[step<<1].val,L[step<<1|1].val);
}
int query(int step,int l,int r){
	if(L[step].left>r||L[step].right<l)
		return inf;
	if(L[step].left>=l&&r>=L[step].right)
		return L[step].val;
	if(L[step].left<L[step].right)
		return min(query(step<<1,l,r),query(step<<1|1,l,r));
	return inf;
}
void slove(int mod){
	int l=0,r=mod-1,ans=-1,temp,k;
	while(l<=MAX){
		if(r>MAX)
			r=MAX;
		temp=query(1,l,r);		
		if(temp!=inf){
			if(ans==-1||(temp%mod)<(ans%mod))
				ans=temp;
			else if((temp%mod)==(ans%mod)&&pos[temp]>pos[ans])
				ans=temp;
		}
		l+=mod;
		r+=mod;
	}
	printf("%d\n",pos[ans]);
}
void fun(int mod){
	int ans=inf,k;
	for(int i=cnt-1;i>=1;i--){
		if(val[i]%mod==0){
			k=i;
			break;
		}
		if(val[i]%mod<ans){
			ans=val[i]%mod;
			k=i;
		}
	}
	printf("%d\n",k);
}
int main(){
	int q,m,tt=0;
	char str[5];
	while(scanf("%d",&q)&&q){
		if(tt>0)
			printf("\n");
		printf("Case %d:\n",++tt);
		bulid(1,0,MAX);
		cnt=1;
		for(int i=0;i<q;i++){
			scanf("%s",str);
			if(str[0]=='B'){
				scanf("%d",&m);
				val[cnt]=m;
				pos[m]=cnt++;
				update(1,m);
			}
			else{
				scanf("%d",&m);
				if(cnt==1)
					printf("-1\n");
				else if(m<=5000)
					fun(m);
				else
			    	slove(m);
			}
		}
	}
	return 0;
}




你可能感兴趣的:(c,工作,query,fun)