回溯法实现01背包问题(递归版)
老师今天让我们上机实验做这个,还是有点把握的。毕竟认真听课了嘛。回溯法其实就是套公式(个人理解)。
来自清华大学出版社的《算法与分析》的解题步骤
(1) 针对所给问题,定义问题的解空间;
(2)确定易于搜索的解空间结构(解空间树);
(3)以深度优先的方式搜索解空间树,并且在搜索过程中用剪枝函数避免无效搜索;
直接贴代码了,里面有较为详细的注释~
package bag;
import java.text.DecimalFormat;
import java.util.Arrays;
public class bagTest {
public static void main(String []args){
double c=20;
int n=10;
double []weight={5,5,3,4,5,1,2,4,3,2};
double []value={5,4,3,2,1,10,1,5,4,3};
/* double []weight=new double[n];
double []value=new double[n];
for(int i=0;i<n;i++){
普通双精度随机数
* double random1=Math.random()*100;
double random2=Math.random()*100;
//只有两位小数的双精度数
double random1=(double)Math.round((Math.random()*10+1)*100)/100;
double random2=(double)Math.round((Math.random()*10+1)*100)/100;
weight[i]=random1;
value[i]=random2;
}*/
bag b=new bag(c,n,weight,value);
}
}
class bag{
private double c;
private int n;
private double []weight;
private double []value;
//存储排序后的编号
private int []x;
//最终选择的物品组
private StringBuffer result;
//完整解(最大价值)
private double max;
//部分解(当前最大值)
private double pv;
//当前重量
private double pw;
public bag(double c ,int n,double []weight, double []value){
this.c=c;;
this.n=n;
this.weight=weight;
this.value=value;
x=new int[n];
result=new StringBuffer();
max=0;
pv=0;
pw=0;
sort();
backtrack(0);
}
private void sort() {
Element[] e = new Element[n];
for (int i = 0; i < n; i++) {
e[i] = new Element(value[i], weight[i], i);
}
Arrays.sort(e);
// 将排好序的物品按顺序重新放入value,weight
for (int i = 0; i < n; i++) {
x[i]=e[i].getI();
value[i] = e[i].getV();
weight[i] = e[i].getW();
}
//测试排序是否正确
for(int i=0;i<n;i++)
System.out.println(x[i]+” “+value[i] +” “+ weight[i]);
}
private void backtrack(int t){
if(t>n-1){max=pv; print(); return;}
else{
if(bound(t)){
if(constraint(t)){
pv+=value[t];
pw+=weight[t];
result.append(x[t]+” “);
backtrack(t+1);
}else{
return;
}
}else{
backtrack(t+1);
}
}
}
//打印选择的物品
private void print() {
DecimalFormat df=new DecimalFormat(“.##”);
String st=df.format(max);
System.out.println(” 总价值为:”+st+” 选取的物品序号分别是:”+result);
}
//剪枝函数(当“当前价值”+“剩余其他物品的总价值”<=”当前最优解”,就不用递归下去了)
private boolean constraint(int t) {
double vResidue=0;
for(int i=t+1;i<n;i++){
vResidue+=value[i];
}
if(max>=(pv+vResidue)) return false;
return true;
}
//限界函数(“剩余背包容量”<0,就不用递归下去了)
private boolean bound(int t) {
//总容量-当前重量和即将放入背包物品的重量=剩余背包容量
double wResidue=c-pw-weight[t];
System.out.println(wResidue);
if(wResidue<0)return false;
return true;
}
}
//制定 元素类,保存旧编号,价值,重量
class Element implements Comparable{
private int i;
private double w,v;
public Element(double v,double w,int i){
this.w=w;
this.v=v;
this.i=i;
}
public double getW() {
return w;
}
public int getI() {
return i;
}
public double getV() {
return v;
}
//从大到小排序
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
double vw=((Element)o).getV()/((Element)o).getW();
double tvw=this.getV()/this.getW();
if(tvw<vw) return 1;
if(tvw==vw) return 0;
return -1;
}
}
———————————————————————————– 升级版 —————————————————————————————————–
同样是根据价值比排列,这次只排列编号,其他保持不动
package bag;
import java.text.DecimalFormat;
import java.util.Arrays;
public class bagT {
public static void main(String []args){
double c=20;
int n=10;
/* 测试用例
* double []weight={5,5,3,4,5,1,2,4,3,2};
double []value={5,4,3,2,1,10,1,5,4,3};
*/
double []weight=new double[n];
double []value=new double[n];
for(int i=0;i<n;i++){
/* 普通双精度随机数
* double random1=Math.random()*100;
double random2=Math.random()*100; */
//只有两位小数的双精度数
double random1=(double)Math.round((Math.random()*10+1)*100)/100;
double random2=(double)Math.round((Math.random()*10+1)*100)/100;
weight[i]=random1;
value[i]=random2;
}
bag1 b=new bag1(c,n,weight,value);
}
}
class bag1{
private double c;
private int n;
private double []weight;
private double []value;
private double []perPrice;
//存储排序后的编号
private int []x;
//最终选择的物品组
private StringBuffer result;
//完整解(最大价值)
private double max;
//部分解(当前最大值)
private double pv;
//当前重量
private double pw;
public bag1(double c ,int n,double []weight, double []value){
this.c=c;;
this.n=n;
this.weight=weight;
this.value=value;
x=new int[n];
for(int i=0;i<n;i++)
x[i]=i;;
perPrice=new double[n];
for(int i=0;i<n;i++)
perPrice[i]=value[i]/weight[i];
result=new StringBuffer();
max=0;
pv=0;
pw=0;
sort(0,n-1);
/* //测试排序是否正确
for(int i=0;i<n;i++){
int j=x[i];
System.out.println(j+” “+value[j] +” “+ weight[j]+” “+ perPrice[i]);
}*/
backtrack(0);
}
private void sort(int l,int r) {
// TODO Auto-generated method stub
if(l<r){
int q=position(l,r);
sort(l,q-1);
sort(q+1,r);
}
}
private int position(int l,int r){
int i=l,j=r+1;
double a=perPrice[l];
int b=x[l];
while(true){
while(perPrice[++i]>a&&i<r);
while(j>l&&perPrice[–j]<a);
if(i>=j)break;
double t1=perPrice[i];
perPrice[i]=perPrice[j];
perPrice[j]=t1;
int t2=x[i];
x[i]=x[j];
x[j]=t2;
}
perPrice[l]=perPrice[j];
perPrice[j]=a;
x[l]=x[j];
x[j]=b;
return j;
}
private void backtrack(int t){
if(t>n-1){max=pv; print(); return;}
else{
if(bound(t)){
if(constraint(t)){
pv+=value[x[t]];
pw+=weight[x[t]];
result.append(x[t]+” “);
backtrack(t+1);
}else{
return;
}
}else{
backtrack(t+1);
}
}
}
//打印选择的物品
private void print() {
DecimalFormat df=new DecimalFormat(“.##”);
String st=df.format(max);
System.out.println(” 总价值为:”+st+” 选取的物品序号分别是:”+result);
}
//剪枝函数(当“当前价值”+“剩余其他物品的总价值”<=”当前最优解”,就不用递归下去了)
private boolean constraint(int t) {
double vResidue=0;
for(int i=t+1;i<n;i++){
vResidue+=value[x[t]];
}
if(max>=(pv+vResidue)) return false;
return true;
}
//限界函数(“剩余背包容量”<0,就不用递归下去了)
private boolean bound(int t) {
//总容量-当前重量和即将放入背包物品的重量=剩余背包容量
double wResidue=c-pw-weight[x[t]];
System.out.println(wResidue);
if(wResidue<0)return false;
return true;
}
}