背包问题有许多种形式,最简单的背包问题形式:现在有一堆石头,(比如重量为2,6,8,10),一个背包中可以装指定的重量(比如14)的石头,请问背包中可以放入的石头的组合。
代码中假设石头是个源数组,背包是目标数组。
算法中使用分治的想法将此问题递归为两个小范围的问题。
针对第n个石头,背包问题可以分解为两种组合的何:含第n个石头的背包的组合,与不含n个石头的背包的组合,
1.假设含第n个石头,背包问题变为,针对剩下的石头求得总重量减去第n个石头的重量的背包问题。
2.不含n个石头,背包问题变为针对剩下的石头求得总重量的背包问题。
class Bag {
public static void main(String[] args) {
int[] src = {2,4,5,7,8,6,10};
select(14,src, 0, new int[src.length]);
}
/**
* @param total 总数
* @param src 源数据数组
* @param offset 源数组的起始位置
* @param bag 背包,装选中的数据
*/
private static void select(int total, int [] src, int offset, int[] bag) {
if(total == 0) { //背包要求增加的重量为0,则直接打印背包
print(bag);
return;
}
//从起始值开始寻找第一个小于要求增加重量的数值
while(offset < src.length && total < src[offset]) offset++;
if(offset >= src.length) return; //当没有源数据可以选择,则退出
//将问题化简为在剩下的源数据中,两个找到重量的问题
select(total,src,offset+1,bag.clone()); //背包中不含有offset位置的
select(total-src[offset],src,offset+1,put(bag,src[offset]));//背包中含有offset位置的
}
private static int[] put(int[] bag, int n) { //将数字放入背包中
int pos = -1;
while(bag[++pos] > 0); //找到背包中第一个数字为0的位置
bag[pos] = n;
return bag;
}
private static void print(int[] bag) { //打印背包中的数字
System.out.print("bag: ");
for(int n: bag) {
if(n == 0) break;
System.out.print(n + " ");
}
System.out.println();
}
}