用回溯法解决0-1背包问题

1)、算法原理

    回溯法解问题时,应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。定义了问题的解空间后,还应将解空间很好地组织起来,使得能用回溯法方便地搜索整个解空间。通常将解空间组织成树或图的形式。

确定了解空间的组织结构后,回溯法从开始结点(根结点)出发,以深度优先方式搜索整个解空间。这个开始结点成为活结点,同时也成为当前的扩展结点。在当前的扩展节点处,搜索向纵深方向移至一个新结点。这个新结点就成为新的活结点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。回溯法以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已无活结点时为止。

2)、解决0-1背包算法分析

0-1背包问题是子集选取问题。一般情况下,0-1背包问题是NP难的。0-1背包问题的解空间可用子集树表示。在搜索解空间树时,只要其左儿子结点是一个可行结点,搜索就进入左子树。当右子树中有可能包含最优解时才进入右子树搜索。否则将右子树剪去。设r是当前剩余物品价值总和;cp是当前价值;bestp是当前最优价值。当cp+r<=bestp时,可剪去右子树。计算右子树中解的上界的更好方法是将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。由此得到的价值是右子树中解的上界。为了便于计算上界,可先将物品依其单位重量价值从大到小排列,此后只要按顺序考察各物品即可。

(3)、算法实现

 

import javax.swing.*;

 

public class Knapsack extends JFrame{
 private int c;//背包的容量
 public int bestp;//最优值
 public int cp;//当前价值
 public int cw;//当前重量
 public static int []flag;//保存原有的位置
 
 
 final JScrollPane scrollPane = new JScrollPane();
 public static JTextArea resulttextArea;
 public JLabel l1 = new JLabel("最优解");
 public JLabel l3 = new JLabel("所用时间");
 public static JTextField t1 = new JTextField();
 public static JTextField t2 = new JTextField();
 final JLabel label = new JLabel();
 final JLabel label_1 = new JLabel();

// private static int x[]; // 记录可选的物品

 private static int[] cx;
 //构造函数
 public Knapsack(int pp[], int ww[], int cc) {
  this.c = cc;
  this.cp = 0;
  this.cw = 0;
  this.bestp = 0;
  cx = new int[pp.length];
  for(int i = 0;i<pp.length;i++){
   cx[i] = 0;
  }
  
 }


 void knapsack(int v[],int w[],int c,int n) {
  double temp = 0;
  int temp1;
  int valuetemp = 0;
  int weighttemp = 0;
  int j;
  flag= new int[50];
  double []t = new double[n+1];
//  计算物品的单位价值
  for(int i = 0; i< n; i++){
   flag[i] = i;
   t[i] =(double) v[i]/w[i];
  }
//  对物品的价值进行排序,并且将价值和重量在数组中也进行重新排序
  for(int i = 0;i<n;i++){
   for( j = n-1; j>i;j--){
    if(t[j] > t[j-1]){
        //交换单位价值
     temp = t[j];
     t[j] = t[j-1];
     t[j-1] = temp;
     //交换价值对应的位置
     valuetemp = v[j];
     v[j] = v[j-1];
     v[j-1] = valuetemp;
     //交换重量对应的位置
     weighttemp = w[j];
     w[j] = w[j-1];
     w[j-1] = weighttemp;
     
     temp1 = flag[j];
     flag[j] = flag[j-1];
     flag[j-1] = temp1;
    }
   }
  }
  backtrack(v,w,c,0,n,cx);
  
  this.setResizable(false);
  this.setTitle("回溯算法计算0-1背包");
  this.getContentPane().setLayout(null);
  this.setBounds(100, 100, 670, 400);
  this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  
  
  scrollPane.setBounds(190, 25, 454, 293);
  getContentPane().add(scrollPane);

  resulttextArea = new JTextArea();
  resulttextArea.setEditable(false);
  scrollPane.setViewportView(resulttextArea);
  
  
  label.setHorizontalTextPosition(SwingConstants.RIGHT);
  label.setHorizontalAlignment(SwingConstants.RIGHT);
  label.setText("最优解:");
  label.setBounds(10, 42, 66, 18);
  getContentPane().add(label);
  
  t1.setHorizontalAlignment(SwingConstants.RIGHT);
 // t1.setHorizontalTextPosition(SwingConstants.RIGHT);
  t1.setHorizontalAlignment(SwingConstants.RIGHT);
  t1.setBounds(80, 42, 66, 18);
  getContentPane().add(t1);
  
  
  label_1.setHorizontalTextPosition(SwingConstants.RIGHT);
  label_1.setHorizontalAlignment(SwingConstants.RIGHT);
  label_1.setText("所用时间:");
  label_1.setBounds(10, 75, 66, 18);
  getContentPane().add(label_1);
  
  t2.setHorizontalAlignment(SwingConstants.RIGHT);
  // t1.setHorizontalTextPosition(SwingConstants.RIGHT);
  t2.setHorizontalAlignment(SwingConstants.RIGHT);
  t2.setBounds(80, 75, 66, 18);
  getContentPane().add(t2);
  
  
 }
   //计算上界值
 double bound(int []v,int []w, int c, int i, int n){  
     int cleft = c - cw;  
     int vleft = cp;  
     //以单位重量价值递减序装入物品
     while(i <= n && w[i] <= cleft){  
         cleft -= w[i];  
         vleft += v[i]; 
         ++i;  
     }  
     //装满背包
     if(i <= n)  
         vleft += v[i] * cleft / w[i]; 
     return vleft;  
 }  
 void backtrack(int v[],int w[], int C, int i, int n, int []cx){  
     //判断是否达到叶子节点
  if(i >= n){  
         if(bestp < cp){  
             bestp = cp;     
         }  
         return;  
     }  
     if(cw + w[i] <= C){  
         cw += w[i];  
         cp += v[i];
         cx[i]=1;
         backtrack(v,w,c,i+1,n,cx);  
         cw -= w[i];  
         cp -= v[i];     
     }  
     if(bound(v,w,C,i,n) > bestp) {
      cx[i]=0;
         backtrack(v,w,C,i+1,n,cx);
     }
 }


 void printResult() {//System.out.println("count"+count);
  System.out.println("*****回溯法*****");
  System.out.println("*****物品个数:n=50");
  System.out.println("*****背包容量:c=1000");
  System.out.println("*****最优值:=" + bestp);
  t1.setText(""+bestp);
  System.out.println("*****选中的物品是:");
 }

 public static void main(String[] args) {
  int p[] = { 220, 208, 198, 192, 180, 180, 165, 162, 160, 158, 155,
    130, 125, 122, 120, 118, 115, 110, 105, 101, 100, 100, 98, 96,95, 90, 88, 82, 80, 77
    , 75, 73, 70, 69, 66, 65, 63, 60, 58, 56,50, 30, 20, 15, 10, 8, 5, 3, 1, 1
     };
  int w[] = { 80, 82, 85, 70, 72, 70, 66, 50, 55, 25, 50, 55, 40, 48,
    50, 32, 22, 60, 30, 32, 40, 38, 35, 32, 25, 28, 30, 22, 50, 30,
    45 , 30 , 60, 50, 20,65, 20, 25, 30, 10, 20, 25, 15, 10, 10, 10,4, 4, 1, 1};

  
  int maxweight = 1000;
  
  double start = System.currentTimeMillis();
  Knapsack ks = new Knapsack(p, w, maxweight);
  ks.knapsack(p,w,1000,50); //回溯搜索
  ks.printResult();
  ks.setVisible(true);
 
  
  resulttextArea.append("物品的选择(‘0’代表没有选择,‘1’代表选择)如下:/n");
  for (int i = 0; i < cx.length; i++) {
   System.out.print(cx[i] + " ");
   resulttextArea.append("x["+flag[i]+"]="+cx[i]+"/n");
  }
  
  double end = System.currentTimeMillis();
  t2.setText((end - start)+"ms");
  System.out.println("/nTime consuming: " + (end - start));
 }
}

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值