概述
给定N个物品和一个容量为C的背包,物品i的重量是Wi
,其价值为Vi
,背包问题是如何选择入背包的物品,使得装入背包的物品的总价值最大,但不能超过总容量。在背包问题中可以将物品的一部分装入背包(物品可以拆分的装入背包),但不能重复装入。
解题思路
用贪心法求解背包问题的关键是如何选定贪心策略,使得按照一定的顺序选择每个物品,并尽可能的装入背包,直到背包装满。
至少有三种看似合适的贪心策略。
选择价值最大的物品,放入背包。
因为这可以尽可能快的增加背包的总价值,但是,虽然每一步选择获得了背包价值的极大增长,但背包容量却可能消耗的太快,使得装入背包的物品个数减少,从而不能保证目标函数达到最大。
选择重量最轻的物品,放入背包。
因为这可以装入尽可能多的物品,从而增加背包的总价值。但是,虽然每一步选择使背包的容量消耗的慢了,但背包的价值却没能保证迅速的增长,从而不能保证目标函数达到最大。
单位重量价值最大的物品,放入背包。
最大价值和最大重量两种贪心策略或者只考虑背包价值的增长,或者只考虑背包容量的消耗,而为了求得背包问题的最优解,需要在背包价值增长和背包容量消耗二者之间寻找平衡。正确的贪心策略是选择单位重量价值最大的物品。
例如:有三个物品,其重量分别为{20,30,10},价值分别为{60,120,50},背包的容量为50,应用三种贪心策略装入背包的物品和获得的价值如下图所示:

程序代码
程序思想:首先计算每种物品单位重量的价值Vi/Wi
,然后,依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包。若将这种物品全部装入背包后,背包内的物品总重量未超过C,则选择单位重量价值次高的物品并尽可能多地装入背包。依此策略一直地进行下去,直到背包装满为止。
伪代码
1 2 3 4 5 6 7 8 9 10 11
| Knapsack(v,w,W,x,n) x <- 0 c <- W for i <- 1 to n do if w[i] ≤ c then x[i] <- 1 c <- c - w[i] if i ≤ n then x[i] <- c/w[i]
return x
|
对N个物品按其单位重量价值从大到小进行排序,排序的时间复杂度为O(nlog2n
),整个算法最耗时的也是这个排序。
Java代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
| package demo1;
public class Knapsack {
private float m; private float[] v; private float[] w; private float[] x; private int n; double[] p_w_v; public static void main(String[] args) throws Exception { Knapsack ksp = new Knapsack();
ksp.knapsack(); } public Knapsack() { this.m = 50.0f; this.v = new float[] { 60.0f, 120.0f, 50.0f }; this.w = new float[] { 10.0f, 30.0f, 20.0f }; this.x = new float[3]; this.n = 3; this.p_w_v = new double[n]; }
public void sort(int n, float[] v, float[] w) throws Exception { for (int i = 0; i < n; i++) { p_w_v[i] = v[i] / w[i]; } System.out.println("单位重量价值:"); print(p_w_v); System.out.println(""); insertSort(p_w_v, w, v); print(p_w_v); }
public void print(double a[]) { int len = a.length; for (int i = 0; i < len; i++) { System.out.print(a[i] + " "); } } public void print(String str) { System.out.println(str); }
public void insertSort(double[] a, float[] b, float[] c) { int len = a.length; double temp; float w_temp; float v_temp; for (int i = 0; i < len - 1; i++) { for (int j = 0; j < len - 1 - i; j++) { if (a[j + 1] > a[j]) {
temp = a[j + 1]; w_temp = w[j + 1]; v_temp = v[j + 1];
a[j + 1] = a[j]; w[j + 1] = w[j]; v[j + 1] = v[j];
w[j] = w_temp; v[j] = v_temp; a[j] = temp; } } } }
public void knapsack() throws Exception { sort(n, v, w); int i; for (i = 0; i < n; i++) { x[i] = 0; } float c = m; for (i = 0; i < n; i++) { if (w[i] > c) { break; } x[i] = 1; c -= w[i]; } if (i < n) { x[i] = c / w[i]; } System.out.println(); print("物品一可以装入的比例: " + x[0]); print("物品二可以装入的比例: " + x[1]); print("物品三可以装入的比例: " + x[2]); print("可以装入的最大价值为: " + (x[0] * v[0] + x[1] * v[1] + x[2] * v[2])); print("可以装入的最大重量为" + (x[0] * w[0] + x[1] * w[1] + x[2] * w[2])); } }
|
C++代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include "stdafx.h" #include <iostream> using namespace std;
const int N = 3;
void Knapsack(int n,float M,float v[],float w[],float x[]);
int main() { float M = 50; float w[] = {0,10,20,30}; float v[] = {0,60,100,120};
float x[N+1];
cout<<"背包所能容纳的重量为:"<<M<<endl; cout<<"待装物品的重量和价值分别为:"<<endl; for(int i=1; i<=N; i++) { cout<<"["<<i<<"]:("<<w[i]<<","<<v[i]<<")"<<endl; } Knapsack(N,M,v,w,x);
cout<<"选择装下的物品比例如下:"<<endl; for(int i=1; i<=N; i++) { cout<<"["<<i<<"]:"<<x[i]<<endl; }
return 0; }
void Knapsack(int n,float M,float v[],float w[],float x[]) { int i; for (i=1;i<=n;i++) { x[i]=0; }
float c=M; for (i=1;i<=n;i++) { if (w[i]>c) { break; } x[i]=1; c-=w[i]; }
if (i<=n) { x[i]=c/w[i]; } }
|