【算法实践】1.3 编程-Halloween treats(鸽巢原理)

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,则一定有解,证明如下:

  1. 假设输入为 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]<=c1

  2. 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 的倍数;

  3. 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BIT可达鸭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值