题目:把N个鸡蛋放到M个篮子里,每个篮子不能为空,要求满足:任意给出一个不超过N的数量,都能找到其中某几个篮子的鸡蛋和等于它。
请写一个程序,输入N,M,然后输出所有的鸡蛋放法。
题目解释:例如6个鸡蛋放3个篮子的一种可能为1,2,3,任意给出1<=x<=6的值,都可以找到1,2,3中的组合的和等于x.
该题目最早是我在网上看到一道600个鸡蛋放在10个篮子的放法,答案是给出了一个按2的乘积放的特例。我将其改编后用来招聘时考察工程师上机编程技能。
解题思路如下:
假设第一个篮子放一个鸡蛋:
那么1,X2中,X2可以放鸡蛋的范围是1<= X2<=2, 如果X2=3,便满足不了给出n=2的情况了
取X2=2
那么1,2,X3中,X3可以放鸡蛋的范围是2<= X3<=4
......
可以推出数学公式:在前m个篮子满足要求时,第m+1个篮子可以放置的鸡蛋数范围应该是: Xm<=Xm+1<=N+1
该范围同时也是搜索解的空间,比较好用递归实现,对于每找到一个符合范围的Xm,可以进一步深度遍历寻找下一个Xm+1, 由此便容易理解标准答案的实现了。
如果使用蛮力计算出所有组合,再逐一过滤筛选也可以实现,但是程序肯定要比上面繁琐,效率较低。
参考答案(保存成html运行即可):
<script>
var n=20,m=5;
var total=0;
var arr = new Array(m);
function exerun(j,t,s){
for(var i=j;i<t+2;i++){
if(s==m-1){
if(n-t<t+2&&n-t>=j){
arr[s]=n-t;
document.writeln(arr+"<br>");
total++;
}
break;
}else if(t+i<n){
arr[s]=i;
exerun(i,t+i,s+1);
}else break;
}
}
exerun(1,0,0);
document.writeln("total:"+total);
</script>
思维延伸:对于太巨大的数字会超出单台机器的计算局限导致缓慢,对次,可考虑采用并行计算方式设计算法,分解到不同机器计算,再合并结果,留给读者去思考。
淘宝分布式并行计算框架fourinone(java实现)下载地址:
http://www.skycn.com/soft/68321.html
使用MapReduce又该怎样实现呢?
其他参考:
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 < M,明显有篮子为空,因而不符。
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)) < N 表示之后的所有篮子都添加相应的最大值,如果这都小于N,那么肯定也不符。
其中(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......(即这里取的都是Sn + 1)
依次类推,我们发现n + 1 + (2n + 2) + (4n + 4) + ...... = (2^count - 1)*(n + 1),count表示相应的篮子数量。
5.代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
1 #include <stdio.h> 2 #include <stdlib.h> 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 < M; 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 < M || N >= 1<<M || M <= 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 }
转载出处:http://www.cnblogs.com/shuaiwhu/archive/2012/06/20/2555541.html