热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

N个鸡蛋放进M个篮子问题

题目:有N个鸡蛋和M个篮子,把鸡蛋放到M个篮子里,每个篮子都不能为空。另外,需要满足:任意一个小于N的正整数,都能由某几个篮子内蛋的数量相加的和得到。写出程序,使得输入一个(N,M),输出所有可能的分

题目:
有N个鸡蛋和M个篮子,把鸡蛋放到M个篮子里,每个篮子都不能为空。另外,需要满足:任意一个小于N的正整数,都能由某几个篮子内蛋的数量相加的和得到。写出程序,使得输入一个(N,M),输出所有可能的分配情况。

 

从题意中应该可以得出,对于(1,1,2,2)和(1,2,1,2)这两种组合,应该是一样的。
因而对于这M个篮子中的鸡蛋数量,我们用数组basket[M]来表示,我们按照非递减顺序进行排列,即basket[i] <= basket[i+1]

1.我们利用归纳法来总结出一个规律:
   对于前n个篮子,其鸡蛋数量总和为Sn,那么对于第n+1个篮子,其鸡蛋数量应该满足:
   basket[n+1] <= Sn + 1,如果basket[n+1] > Sn + 1,那么Sn + 1这个数将无法通过相应的篮子鸡蛋数相加来获得。
   由于是非递减序列,因而
   basket[n] <= basket[n+1] <= Sn + 1

 

2.我们来证明符合上式的数组能够满足条件“任意一个小于N的正整数,都能由某几个篮子内蛋的数量相加的和得到”。
   当M = 1时,basket[0] = 1,当M=2时,取basket[1] = 1,能够满足上述条件。
                                                         取basket[1] = 2,也能够满足上述条件。
   假设M = n-1时,满足上述条件,我们来证明当M = n时亦满足。
   前n-1个篮子的鸡蛋数量总和为Sn-1,此时加上第n个篮子,总和为Sn = Sn-1 + basket[n-1]。即证明Sn - 1,Sn - 2,Sn - 3,Sn - (basket[n-1] - 1)都可以由某几个篮子内蛋的数量相加的和得到。由于basket[n-1] <= Sn-1,而且小于或者等于Sn-1的数能由某几个篮子内蛋的数量相加的和得到,所以Sn - 1,Sn - 2,Sn - 3,Sn - (basket[n-1] - 1)亦可得到。
   证毕。

3.对于N和M的值,我们在输入后即可做一个判断。
   2.1  当N    2.2  当N >= M时,第一个篮子必然要放1个鸡蛋,其后面的篮子我们按照basket[n] <= basket[n+1] <= Sn + 1取最大值,依次为2,4,8,16......,鸡蛋总数为2^M - 1,即M个篮子的鸡蛋数量最大值。

   所以M <= N <2^M

 

4.代码要点

   4.1 对于函数

void solve(int current_sum, int basket_id, int current_num, int* basket, int N, int M)

   其中current_sum表示当前所有篮子鸡蛋的总和,

         basket_id表示当前篮子的序号,
         current_num表示将要放到当前篮子去的鸡蛋数量,
         basket, N, M值都是main函数中的原值,在递归中这三个参数基本没变。
   初始化为(0, 0, 1, basket, N, M)表示此时所有鸡蛋数量为0,将要把1个鸡蛋放进第0个篮子里面。

   4.2 对于函数solve中的

    if ((current_sum + current_num*(M - basket_id)) > N || (current_sum + (current_sum + 1)*((1<<(M - basket_id)) - 1)) < N)
return;

         我们采用的是搜索中的剪枝技术,即在每次递归时,通过预先判断来看此路是否走得通。

  (current_sum + current_num*(M - basket_id)) > N 表示之后的所有篮子都添加最小鸡蛋数量,如果这都大于N,那么肯定不符。
  (current_sum + (current_sum + 1)*((1<<(M - basket_id)) - 1))  

        其中(current_sum + 1)*((1<<(M - basket_id)) - 1) 可以这样解释:

        假设前面的篮子总和为n,那么紧挨着的后一个篮子里鸡蛋数量最大值为n+1,其后的一个篮子最大值为n + (n + 1) + 1 = 2n + 2,这之后的一个篮子的最大值为n + (n + 1) + (2n + 2) + 1 = 4n + 4......(即这里取的都是S+ 1)
        依次类推,我们发现n + 1 + (2n + 2) + (4n + 4) + ...... = (2^count - 1)*(n + 1),count表示相应的篮子数量。

 

5.代码如下:

N eggs M baskets
 1 #include 
2 #include
3
4 void solve(int current_sum, int basket_id, int current_num, int* basket, int N, int M)
5 {
6 if (current_sum == N && basket_id == M)
7 {
8 int i;
9 for (i = 0; i )
10 printf("%d\t", basket[i]);
11 printf("\n");
12 return;
13 }
14
15 if (current_num > N || basket_id >= M)
16 return;
17
18 if ((current_sum + current_num*(M - basket_id)) > N ||
19 (current_sum + (current_sum + 1)*((1<<(M - basket_id)) - 1)) < N)
20 return;
21
22 int j;
23 for (j = current_num; j <= current_sum + 1; j++)
24 {
25 basket[basket_id] = j;
26 solve(current_sum + j, basket_id + 1, j, basket, N, M);
27 }
28 }
29
30 int main()
31 {
32 int N;//the number of eggs
33 int M;//the number of baskets
34 while (scanf("%d%d", &N, &M) != EOF)
35 {
36 if (N = 1<0)
37 printf("Wrong data!\n");
38 else
39 printf("The combinations are as below:\n");
40
41 int* basket = (int*)malloc(sizeof(int)*M);
42 solve(0, 0, 1, basket, N, M);
43 free(basket);
44 }
45 return 0;
46 }

 

 1 #include 
2
3 void solve(int current_sum, int basket_id, int current_num, int* basket, int N, int M)
4 {
5 if (current_sum == N && basket_id == M)
6 {
7 int i;
8 for (i = 0; i )
9 printf("%d\t", basket[i]);
10 printf("\n");
11 return;
12 }
13
14 if (current_num > N || basket_id >= M)
15 return;
16
17 if ((current_sum + current_num*(M - basket_id)) > N ||
18 (current_sum + (current_sum + 1)*((1<<(M - basket_id)) - 1)) < N)
19 return;
20
21 int j;
22 for (j = current_num; j <= current_sum + 1; j++)
23 {
24 basket[basket_id] = j;
25 solve(current_sum + j, basket_id + 1, j, basket, N, M);
26 }
27 }
28
29 int main()
30 {
31 int N;//the number of eggs
32 int M;//the number of baskets
33 while (scanf("%d%d", &N, &M) != EOF)
34 {
35 if (N = 1<0)
36 printf("Wrong data!\n");
37 else
38 printf("The combinations are as below:\n");
39
40 int* basket = new int[M];
41 solve(0, 0, 1, basket, N, M);
42 delete[] basket;
43 }
44 return 0;
45 }

推荐阅读
  • Codeforces Round #566 (Div. 2) A~F个人题解
    Dashboard-CodeforcesRound#566(Div.2)-CodeforcesA.FillingShapes题意:给你一个的表格,你 ... [详细]
  • 本题通过将每个矩形视为一个节点,根据其相对位置构建拓扑图,并利用深度优先搜索(DFS)或状态压缩动态规划(DP)求解最小涂色次数。本文详细解析了该问题的建模思路与算法实现。 ... [详细]
  • 使用GDI的一些AIP函数我们可以轻易的绘制出简 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 本文详细探讨了C语言中指针的概念,特别是指针在变量和数组中的应用。通过实例讲解,帮助读者更好地掌握指针的使用方法。 ... [详细]
  • 本文介绍了几种不同的编程方法来计算从1到n的自然数之和,包括循环、递归、面向对象以及模板元编程等技术。每种方法都有其特点和适用场景。 ... [详细]
  • 本文介绍了一种解决二元可满足性(2-SAT)问题的方法。通过具体实例,详细解释了如何构建模型、应用算法,并提供了编程实现的细节和优化建议。 ... [详细]
  • 数据结构入门:栈的基本概念与操作
    本文详细介绍了栈这一重要的数据结构,包括其基本概念、顺序存储结构、栈的基本操作(如入栈、出栈、清空栈和销毁栈),以及如何利用栈实现二进制到十进制的转换。通过具体代码示例,帮助读者更好地理解和应用栈的相关知识。 ... [详细]
  • C语言基础入门:7个经典小程序助你快速掌握编程技巧
    本文精选了7个经典的C语言小程序,旨在帮助初学者快速掌握编程基础。通过这些程序的实践,你将更深入地理解C语言的核心概念和语法结构。 ... [详细]
  • 本题探讨了在一个有向图中,如何根据特定规则将城市划分为若干个区域,使得每个区域内的城市之间能够相互到达,并且划分的区域数量最少。题目提供了时间限制和内存限制,要求在给定的城市和道路信息下,计算出最少需要划分的区域数量。 ... [详细]
  • 本题涉及一棵由N个节点组成的树(共有N-1条边),初始时所有节点均为白色。题目要求处理两种操作:一是改变某个节点的颜色(从白变黑或从黑变白);二是查询从根节点到指定节点路径上的第一个黑色节点,若无则输出-1。 ... [详细]
  • 毕业设计:基于机器学习与深度学习的垃圾邮件(短信)分类算法实现
    本文详细介绍了如何使用机器学习和深度学习技术对垃圾邮件和短信进行分类。内容涵盖从数据集介绍、预处理、特征提取到模型训练与评估的完整流程,并提供了具体的代码示例和实验结果。 ... [详细]
  • 作为一名专业的Web前端工程师,掌握HTML和CSS的命名规范是至关重要的。良好的命名习惯不仅有助于提高代码的可读性和维护性,还能促进团队协作。本文将详细介绍Web前端开发中常用的HTML和CSS命名规范,并提供实用的建议。 ... [详细]
  • 本文介绍如何使用布局文件在Android应用中排列多行TextView和Button,使其占据屏幕的特定比例,并提供示例代码以帮助理解和实现。 ... [详细]
  • 本文介绍了 Winter-1-C A + B II 问题的详细解题思路和测试数据。该问题要求计算两个大整数的和,并输出结果。我们将深入探讨如何处理大整数运算,确保在给定的时间和内存限制下正确求解。 ... [详细]
author-avatar
小森林
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有