您的位置:首页 > 其它

0-1背包问题

2013-07-15 22:42 239 查看
回溯法:适用于背包容量C远大于物品个数N,时间复杂度O(2*N )

import java.util.Arrays;
import java.util.Scanner;

public class Knapsack {
private Bag[] bags;
private int capacity;
private int[] x;
private int maxValue;
private int currentValue;
private int currentWeight;
int count = 0;

public Knapsack(Bag[] bags, int capacity){
this.bags = bags;
this.capacity = capacity;
this.x = new int[bags.length];
this.maxValue = 0;
this.currentValue = 0;
this.currentWeight = 0;
}

public int getMaxValue(){
//sort the bag in decrease order by value/weight
Arrays.sort(bags);
getMaxValue(0);
return this.maxValue;
}

private void getMaxValue(int level){
count ++;
//find a answer
if(level >= bags.length){
if(currentValue > maxValue)
maxValue = currentValue;
return;
}
//choose the current bag
if(this.currentWeight + bags[level].weight <= this.capacity){
this.x[level] = 1;
this.currentValue += bags[level].value;
this.currentWeight += bags[level].weight;
getMaxValue(level + 1);
this.currentValue -= bags[level].value;
this.currentWeight -= bags[level].weight;
}
//check next bag if currentValue + result of full knapsack > bestValue
if(bound(level + 1)){
this.x[level] = 0;
getMaxValue(level + 1);
}
}

private boolean bound(int level){
float max = this.currentValue;
int available = this.capacity - this.currentWeight;
//use the result of full knapsack with capacity 'available' as the bound
while(level < bags.length && bags[level].weight <= available){
available -= bags[level].weight;
max += bags[level].value;
level++;
}
if(level < bags.length){
max += available * bags[level].price;
}
return max >= this.maxValue;
}

private static class Bag implements Comparable<Bag>{
int weight;
int value;
float price;

public Bag(int weight, int value) {
super();
this.weight = weight;
this.value = value;
this.price = (float)value / (float)weight;
}

@Override
public int compareTo(Bag other) {
if((other.price - this.price) > 0) return 1;
else return -1;
}
}

public static void main(String[] args){
Scanner stdIn = new Scanner(System.in);
while(stdIn.hasNext()){
int capacity = stdIn.nextInt();
if(capacity <= 0) break;
int bagNum = stdIn.nextInt();
Bag[] bags = new Bag[bagNum];
for(int i = 0; i < bagNum; i++){
int weight = stdIn.nextInt();
int value = stdIn.nextInt();
bags[i] = new Bag(weight, value);
}

Knapsack OJ = new Knapsack(bags, capacity);
System.out.println(OJ.getMaxValue());
System.out.println(OJ.count);
}
}
}

分支限界算法:采用优先队列对子集树进行搜索。对于某个结点,上界 = 剩余背包容量的完全背包最优解 + 当前收益。算法不断扩展结点,直至子集树的叶结点成为扩展结点为止。此时,该叶结点相应的解为最优解,优先队列所有活结点的上界值均不超过该叶结点的价值收益(对于该结点,收益上界 = 当前收益)。

public class Knapsack {
private Bag[] bags;
private int capacity;
private int maxValue;
//current expand node
private Node current;
private PriorityQueue<Node> queue;
private boolean[] bestX;

public Knapsack(Bag[] bags, int capacity){
this.bags = bags;
this.capacity = capacity;
this.maxValue = 0;
this.bestX = new boolean[this.bags.length + 1];
this.queue = new PriorityQueue<Node>();
}

public int getMaxValue(){
int level = 0;
Arrays.sort(bags);
this.current = new Node(0, 0, 0, bound(0), false, null);
while(level  != bags.length){
//try to choose the bags[level]
if(current.currentWeight + bags[level].weight <= this.capacity){
if(current.currentValue + bags[level].value > maxValue){
maxValue = current.currentValue + bags[level].value;
}
queue.add(new Node(level + 1, current.currentValue + bags[level].value,
current.currentWeight + bags[level].weight, current.upBound, true, current));
}
//try to discard the bags[level]
float upBound = bound(level + 1);
if( upBound >= maxValue){
queue.add(new Node(level + 1, current.currentValue,
current.currentWeight, upBound, false, current));
}
current = queue.poll();
level = current.level;
}
return current.currentValue;
}

public void showSolution(){
calSolution();
for(int i = 1; i < bags.length; i++){
if(bestX[i]) System.out.printf("%d ", i);
}
if(bestX[bags.length]){
System.out.print(bags.length);
}
System.out.println();
}

private void calSolution(){
while(null != current.parent){
this.bestX[current.level] = current.choose;
current = current.parent;
}
}

private float bound(int level){
float max = 0;
int available = 0;
if(0 == level){
max = 0;
available = this.capacity;
}else{
max = current.currentValue;
available = this.capacity - current.currentWeight;
}
//use the result of full knapsack with capacity 'available' as the bound
while(level < bags.length && bags[level].weight <= available){
available -= bags[level].weight;
max += bags[level].value;
level++;
}
if(level < bags.length){
max += available * bags[level].price;
}
return max;
}

private class Node implements Comparable<Node>{
int level;
int currentValue;
int currentWeight;
float upBound;
boolean choose;
Node parent;

@Override
public int compareTo(Node o) {
if(this.upBound < o.upBound) return 1;
else return -1;
}

public Node(int level, int currentValue, int currentWeight,
float upBound, boolean choose, Node parent) {
super();
this.level = level;
this.currentValue = currentValue;
this.currentWeight = currentWeight;
this.upBound = upBound;
this.choose = choose;
this.parent = parent;
}
}

private static class Bag implements Comparable<Bag>{
int weight;
int value;
float price;

public Bag(int weight, int value) {
super();
this.weight = weight;
this.value = value;
this.price = (float)value / (float)weight;
}

@Override
public int compareTo(Bag other) {
if((this.price- other.price) < 0) return 1;
else return -1;
}
}
}


动态规划:由于大多数情况下,部分子问题不用求解,故适合采用备忘录方法

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Knapsack {
private int W;
private int n;
private int[] w;
private int[] v;
private int[][] f;
private int countCall = 0;

public Knapsack(int W, int n, int[] w, int[] v){
this.W = W;
if(n != w.length || n != v.length){
throw new IllegalArgumentException();
}
this.n = n;
this.w = w;
this.v = v;
this.f = new int[n + 1][W + 1];
}

/**
* 0-1背包问题,对于K相对物品个数n较大的情况,备忘录方法效率更高
* 它可以避免计算一些不可达的子问题空间	 *
* @return the
*/
public int oneZeroKnapsack(){
for(int i = 0; i <= n; i++)
for(int j = 0; j <= W; j++)
f[i][j] = Integer.MAX_VALUE;
return lookup_chain(n , W);
}

public void showObjects(){
int i = n , j = W;
while(i > 0){
if(f[i][j] != f[i-1][j]){
System.out.println(i);
j -= w[i-1];
}
i--;
}
}
private int lookup_chain(int i , int j){
countCall++;
if( Integer.MAX_VALUE != f[i][j]) return f[i][j];
if( i <= 0 || j <= 0 ) return 0;

if(w[i-1] > j){
f[i][j] = lookup_chain(i-1, j);
}
else{
int choose = lookup_chain(i-1, j-w[i-1]) + v[i-1];
int noChoose = lookup_chain(i-1 ,j);
if(choose > noChoose)
f[i][j] = choose;
else f[i][j] =noChoose;
}
return f[i][j];
}
private class Node implements Comparable<Node>{
int weight;
private int value;
int seq;
float price;

public Node(int weight, int value , int seq){
this.weight = weight;
this.value = value;
this.seq = seq;
this.price = value / weight;
}

@Override
public int compareTo(Node o) {
if (o.price - this.price > 0) return 1;
else return -1;
}
}
}


DP算法:对于物品重量较小,且背包容量C=O(N)时,适合采用传统的自底向上的DP算法(时间复杂度为O(CN))。但此场景比较少见,故未给出该算法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息