Description
Every year there is the same problem at Halloween: Each neighbour is only willing to give a certain total number of sweets on that day, no matter how many children call on him, so it may happen that a child will get nothing if it is too late. To avoid conflicts, the children have decided they will put all sweets together and then divide them evenly among themselves. From last year’s experience of Halloween they know how many sweets they get from each neighbour. Since they care more about justice than about the number of sweets they get, they want to select a subset of the neighbours to visit, so that in sharing every child receives the same number of sweets. They will not be satisfied if they have any sweets left which cannot be divided.
题目可以转换为求
n
n
n 个数中
z
z
z 个数之和为
c
c
c 的倍数;
题目中说明了 c < = n c<=n c<=n,则一定有解,证明如下:
-
假设输入为 m = { m 1 , m 2 , … , m n } m=\lbrace m_1,m_2,…,m_n\rbrace m={m1,m2,…,mn} 共 n n n 个数,构造数列 r [ n ] r[n] r[n],其中 r [ j ] r[j] r[j] 为 m m m 的前 j j j 项和对 c c c 取余得到的数,即:
r [ j ] = ( ∑ i = 0 j m i ) % c , j = 1 , … , n r[j]=(\sum^j_{i=0}m_i)\%c,j=1,…,n r[j]=(∑i=0jmi)%c,j=1,…,n,且 0 < = r [ j ] < = c − 1 0<=r[j]<=c-1 0<=r[j]<=c−1;
-
若 c = n c=n c=n:
2.1 若 r [ n ] r[n] r[n] 中 n n n 个数各不相同,则这 n ( n = c ) n(n=c) n(n=c) 个数中一定存在 0 0 0,为 c c c 的倍数;
2.2 若 r [ n ] r[n] r[n] 中 r [ k ] = r [ t ] r[k]=r[t] r[k]=r[t],则一定有 ( ∑ i = k + 1 t m i ) % c = 0 (\sum^t_{i=k+1}m_i)\%c=0 (∑i=k+1tmi)%c=0,此时这些数之和就是 c c c 的倍数; -
若 c < n c<n c<n:
由鸽巢原理可知,一定存在 r [ k ] = r [ t ] r[k]=r[t] r[k]=r[t];
得证;
代码如下:
#include "stdio.h"
#include "string"
#include "map"
#define MAXN 100100
using namespace std;
int coun[MAXN];
int main(void)
{
int n, c, sum, tmp;
// c<=n 一定有解
while (scanf("%d%d", &c, &n) && (c || n))
{
sum = 0;
map<int, int> mmap; // 统计余数是否出现
for (int i = 1; i <= n; ++i)
{
scanf("%d", &coun[i]);
mmap[coun[i]%c] = 0;
}
for (int i = 1; i <= n; ++i)
{
sum += coun[i];
tmp = sum % c;
sum %= c;
if (tmp == 0)
{
// 前 i 项和能被 c 整除
printf("%d", 1);
for (int j = 2; j <= i; ++j)
{
printf(" %d", j);
}
break;
}
else
{
if (mmap[tmp])
{
// 有两项余数相同
printf("%d", mmap[tmp] + 1);
for (int j = mmap[tmp] + 2; j <= i; ++j)
{
printf(" %d", j);
}
break;
}
}
mmap[tmp] = 1;
}
printf("\n");
}
return 0;
}