您的位置:首页 > 其它

递归调用-水池蓄满水可以有多少种组合

2015-06-12 00:00 281 查看
摘要: 假设有一个大水池,其容积为poolSize,还有n个彼此大小不同的水桶,分别是B1,B2,B3,B4,B5..Bn,放在一个数组中 。请给出水桶的所有组合,使得他们的容积之和刚好可以灌满整个水池poolSize

package com.ironman.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* http://supercharles888.blog.51cto.com/609344/1345561 *题目:
假设有一个大水池,其容积为poolSize,还有n个彼此大小不同的水桶,分别是B1,B2,B3,B4,B5..Bn,放在一个数组中 。请给出水桶的所有组合,使得他们的容积之和刚好可以灌满整个水池poolSize

分析:
因为很久没弄算法了,这题目搞了我近2个小时,其实思路还是蛮简单的, 就是回溯。首先,先升序排列水桶,然后移除水桶容积直接超过水池容积的(这种水桶显然不可能成为解的集合) 。
排序后,选出最大的水桶,如果最大的水桶容积刚好等于水池容积,那么返回。
如果最大的水桶容积也没有水池大,那么必定是若干小水桶的组合来满足大水池的要求,假设满足要求的水桶集合为C.这时候,我们从排序了的水桶中选出最大的水桶,这样分2种情况:

假设目前最大的水桶Bmax是C的一部分,那么最终的C必定为Bmax  加上水桶集合中除去Bmax的某个组合来满足C-Bmax的结果集,这是一个递归
假设目前最大的水桶Bmax不是C的一部分,那么最终的C必定为水桶集合中除去Bmax的某个组合来满足C的结果集,这也是一个递归。

* 假设有一个大水池,其容积为poolSize,还有n个彼此大小不同的水桶,分别是B1,B2,B3,B4,B5..Bn,放在一个数组中
* 请给出水桶的所有组合,使得他们的容积之和刚好可以灌满整个水池poolSize
*
* @author charles.wang
*
*/
public class BucketAndPool {
// 放置可能的解
private static List<Integer> list = new ArrayList<Integer>();
/**
* 这是一个wrapper方法,用于做一些参数排序处理,和排除所有绝对不可能作为候选列表的桶,以免减少运行时间开销
*
* @param poolSize
*            池的大小
* @param buckets
*            所有候选桶的数组
*/
public static void fillPoolWithBuckets(int poolSize, int[] buckets) {
// 局部变量,作为真正参与运算的buckets
int[] actualBuckets = buckets;
// 首先,吧所有的水桶按照升序排序,这样方便我们找到最大的桶
Arrays.sort(actualBuckets);
// 判断如果某个buckets的大小超过了池子,那么从这个bucket开始,比它大的buckets都不考虑
// 于是缩减原来的buckets数组为新的buckets数组,并且只有不超过poolSize的buckets会列在其中
for (int i = 0; i < buckets.length; i++) {
if (buckets[i] > poolSize) {
actualBuckets = Arrays.copyOf(actualBuckets, i);
break;
}
}
// 递归的调用方法来输出结果
findSolution(poolSize, actualBuckets);
}
/**
* 递归方法,用当前的可选已经排过序的桶的列表来填充一个池子poolSize
*
* @param poolSize
* @param sortedBuckets
*/
private static void findSolution(int poolSize, int[] sortedBuckets) {
// 递归的出口,是当前没有可用的桶或者需要凑容积的池子尺寸已经为0了
if ((sortedBuckets.length == 0) || (poolSize <= 0))
return;
// 输出结果
//输出结果的条件是当前最大桶(已经排序过)直接满足条件,那么当前最大桶就作为可用解的一个值
//比如用桶{1,2,3}去凑水池容积为3, 那么一旦找到3,那么就直接输出3
//而解访问两部分,一个是已有的在计算子匹配之前的已经选出的桶的列表,在list列表中存着呢
//另一个是找到的当前的解,所以依次输出已选桶的列表(来自list)和当前桶
if (poolSize == sortedBuckets[sortedBuckets.length - 1]) {
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + ",");
}
System.out.println(poolSize);
}

//如果最大桶无法直接满足池子的容量,那么就必须用若干小桶来凑池子的容量,为此,先选出小桶中最大的那个。

// 最大的水桶
int largestBucket = sortedBuckets[sortedBuckets.length - 1];
//现在解决方案分2部分:
//(1)放入可用桶桶列表中最大的这个到可用列表中,并且这个桶是有解的,
//   则用桶列表剔除最大桶的子桶列表来凑 剩余池子容量-最大桶容量,这是个递归调用
//(2)放入最大桶进去之后,是无解的,那么从可用列表中剔除这个最大桶
//   并且用桶列表剔除最大桶的子桶列表来凑 剩余池子容量,这是也个递归调用

//放入最大的水桶到可用列表,递归解决其他的水桶来凑 池容量-最大桶容量
list.add(largestBucket);
findSolution(poolSize - largestBucket,
Arrays.copyOf(sortedBuckets, sortedBuckets.length - 1));
//不放入最大的水桶,递归的解决其他的水桶来凑 池容量
list.remove(list.size() - 1);
findSolution(poolSize,
Arrays.copyOf(sortedBuckets, sortedBuckets.length - 1));
}
public static void main(String[] args) {
int poolSize = 31;
/*我们假设有若干小水桶,6L,8L,7L,12L,28L,23L,1L,3L,16L,我们要凑出31L的水池, 那么运行这个程序之后,结果如下:*/
int[] buckets = { 31,6, 8, 7, 12, 17, 28, 23, 1, 3, 16 };

fillPoolWithBuckets(poolSize, buckets);
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: