问题描述
假设有一个能装入总体积为T的背包和n件体积分别为w1,w2…wn.的物品,能否从n件物品中挑选若干件恰好装满背包,即使w1+w2+…+wn=T,要求找出所有满足上述条件的解.
解决思路
回溯法
假设一个背包可背的重量为s,现在有n件物品,重量分别为w1,w2,…,wn。若选取其中若干件物品的质量总和恰好为s,那么称此背包问题有解,否在无解。
在讨论这个问题时,考虑的是一件物品在背包问题中只有两种可能
一种是不选择wn,这样 Knap(s,n)的解就是Knap(s,n-1)的解;
另一种是选择wn,这样Knap(s,n)的解就是Knap(s-wn,n-1)**的解;
这样可以将背包问题的递归定义为Knap(s,n)
s= 0时,Knap(s,n)=True
s<0时,Knap(s,n)=Fals
s>0且n<1,Knap(s,n)=False
s>0且n>=1,Knap(s,n)=Knap(s,n-1)或Knap(s-wn,n-1)
具体举个例子假设
假设T=10;w={1,8,4,3,5,2};
此时回溯的8
满足条件输出方案:1432
此时回溯的2
此时回溯的3
满足条件输出方案:145
此时回溯的5
此时回溯的2
此时回溯的4
此时回溯的5
此时回溯的2
此时回溯的3
此时回溯的2
此时回溯的5
此时回溯的2
此时回溯的1
满足条件输出方案:82
此时回溯的2
此时回溯的8
此时回溯的2
此时回溯的3
此时回溯的5
此时回溯的2
此时回溯的4
满足条件输出方案:352
此时回溯的2
此时回溯的5
此时回溯的2
此时回溯的3
此时回溯的2
此时回溯的5
此时回溯的2
代码展示
import java.util.Arrays;
public class Package {
static class MyList {
//定义Object类型的数组
Object[] data ;
//统计变量,用于统计数组元素的真实个数
int size;
public MyList() {
//初始化长度为10
this(50);
}
MyList(int length){
//通过构造方法指定数组的长度
data = new Object[length];
}
//长度
public int getLength(){
return size;
}
//为了方便看效果,我们覆写toString()方法
//为了打印真实的数组内容,除去空余的默认值
@Override
public String toString() {
//构建一个新的数组,长度为size
Object[] newdata = new Object[size];
//将data中的元素拷贝到新数组中
System.arraycopy(data, 0, newdata, 0, size);
//利用Arrays类,将数组转换成字符串
return Arrays.toString(newdata);
}
//增
void add(Object obj){
//如果数组满了
if(size>=data.length){
//构建一个新的数组,容量默认增加10
Object[] newdata = new Object[data.length+10];
//将原来的数组内容拷贝到扩容后的数组中
System.arraycopy(data, 0, newdata, 0, size);
}
//将新增的元素添加在数组的末尾
data[size] = obj;
//数组真实长度自增1
size++;
}
//查找指定元素第一次出现的索引
public int indexOf(Object obj){
for (int i = 0; i < size; i++) {
if(obj.equals(data[i])){
return i;
}
}
return -1;//没有找到
}
}
static class MyStack<E> {
//数组的默认大小为10
static final int DEFAULT_INIT_CAPACITY = 10;
//底层的数组
Object[] elements;
//栈中的个数
int size;
public MyStack() {
this(DEFAULT_INIT_CAPACITY);
}
public MyStack(int capacity) {
//capacity条件检查 ,这里我们直接抛出异常
if (capacity <= 0) {
throw new IllegalArgumentException("capacity <= 0");
}
//新建一个capacity大小的数组
elements = new Object[capacity];
//初始个数为0
size = 0;
}
//栈是否为空
public boolean isEmpty() {
return size == 0;
}
//返回栈中的元素个数
public int size() {
return size;
}
//将一个元素压入栈中
public E push(E e) {
//如果栈已满,进行扩容
if (size >= elements.length) {
grow();
}
//扩容完后将元素e压入栈中
elements[size] = e;
//别忘了size需要加 1
size++;
return e;
}
//出栈,就是将数组最后一个元素弹出
public E pop() {
//如果栈为空就返回null
if (isEmpty()) {
return null;
}
//拿到栈的大小
int len = size();
//把数组中最后一个元素保存起来
E e = peek();
//个数别忘了减1
size--;
//将最后一个元素置null
elements[len - 1] = null;
//返回e
return e;
}
//返回最后一个元素
public E peek() {
int len = size();
if (len == 0)
throw new RuntimeException("stack is empty");
return (E) elements[len - 1];
}
//扩容
private void grow() {
//将之前的数组保存
int oldCapacity = elements.length;
Object[] old = elements;
//新的数组大小为原来数组大小的2倍
int newCapacity = oldCapacity * 2;
//再新建一个大小为原来数组2倍的新数组
elements = new Object[newCapacity];
//把以前的老的数组中的元素都移动新数组中
for (int i = 0; i < oldCapacity; i++) {
elements[i] = old[i];
}
//释放以前的内存空间
old = null;
}
}
public static void main(String[] args) {
MyList list = new MyList();
int count = 0;
// Scanner scanner = new Scanner(System.in);
// System.out.println("请输入的物体的体积,输入0停止");
// while (scanner.hasNextLine()) {
// int a = scanner.nextInt();
// if (a == 0) {
// break;
// }
// list.add(a);
// }
// System.out.println("请输入背包的大小");
// int caption = scanner.nextInt();
// scanner.close();
list.add(1);
list.add(8);
list.add(4);
list.add(3);
list.add(5);
list.add(2);
int caption = 10;
int length = list.size;
MyStack<Integer> stack = new MyStack<>();
int j = 0;
do {
for (int i = j; i < length && caption > 0; i++) {
if (caption >= (int)list.data [i])//有点贪婪的思想能放下就放下
{
caption -= (int) list.data[i];
//System.out.println(caption);
stack.push((Integer) list.data[i]);
// System.out.println("此时放置的" + list.get(i));
}
}
if (caption == 0) {//合适我们就输出
count++;
System.out.print("第" + count + "种方案:");
for (Object integer : stack.elements) {
if(integer!=null)
System.out.print(integer);
}
System.out.println();
}
int temp = stack.pop();//不合适我们就回溯
caption += temp;
System.out.println("此时回溯的" + temp);//碰到2就回溯两次
j = list.indexOf(temp) +1;//从回溯的元素下一个元素开始
// System.out.println(j);
} while ((!stack.isEmpty())|| (j != length) );//双指针思想
}
/**
程序=算法+数据结构,但是我觉得他们虽然是相加的关系,但是
它们在对于程序的意义上的地位应该是不相等的,应该是3,7开的
算法占7.数据结构占3,因为一个好的算法能够大大减小计算机解
决问题的开销,虽然好的数据结构也可以,但是算法的用处应该还
是大一点,就像这里和后面的八皇后,你如果采用枚举法的话,运算量
将...**/
}