1. 程式人生 > >【51Nod 1103】N的倍數(字首和,抽屜定理)詳細!!

【51Nod 1103】N的倍數(字首和,抽屜定理)詳細!!

Description

一個長度為N的陣列A,從A中選出若干個數,使得這些數的和是N的倍數。 
例如:N = 8,陣列A包括:2 5 6 3 18 7 11 19,可以選2 6,因為2 + 6 = 8,是8的倍數。

先欣賞一下兩位大佬的程式碼

 字首和:https://blog.csdn.net/qq_38735931/article/details/81590452?utm_source=blogxgwz1

抽屜原理:https://blog.csdn.net/dingchenxixi/article/details/52459001

 

思路:

用一個sum陣列記錄字首和,

先看簡單的:想想如何存在一個i,sum[i]%n==0,是不是以為這前i個數的和是n的倍數,也就滿足題目的要求,輸出即可;

下面就是麻煩一點的:假設所有的sum[i]都不符合上面的條件,那麼現在sum[i]的取值範圍是【1,n-1】,但是i是從【1,n】的,根據抽屜原理,肯定有i!=j, sum[i]==sum[j]存在,這樣他們的差值,也就是前j個數減去前i個數的那些數的和(假設j>i),一定是等於0的,也滿足咱們題目的條件。

如果細心看的話,你是肯定可以看懂的。

AC程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
	int a[50005],sum[50005],book[50005];
	int n,i,j;
	scanf("%d",&n);
	memset(a,0,sizeof(a));
	memset(book,0,sizeof(book));
	memset(sum,0,sizeof(sum));
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		sum[i]=(sum[i-1]+a[i])%n;
	}
	for(i=1;i<=n;i++)
	{
		if(!sum[i])
		{
			printf("%d\n",i);
			for(j=1;j<=i;j++)
			printf("%d\n",a[j]);
			return 0;
		}
		if(book[sum[i]]!=0)//如果前面有一個sum[]等於現在的sum[i]
		{
			printf("%d\n",i-book[sum[i]]);
			for(j=book[sum[i]]+1;j<=i;j++)
			{
				printf("%d\n",a[j]);
			}
			return 0;
		}
		book[sum[i]]=i;//方便記錄某個sum[i]出現位置的下標
	}
//	return 0;
 }