递归、备忘录、自底向上动态规划,计算时间依次减少。
递归方式:
public class KnapsackRecursion {
// private int c;
// private int num;
private int v[];
private int w[];
public int x[];
public KnapsackRecursion(int[] v, int[] w, int c) {
this.v = v;
this.w = w;
x = new int[c];
}
// function Make( i {处理到第i件物品} , j{剩余的空间为j}) :integer;(num-1, c)
public int Knap(int i, int j) {
int r1 = 0;
int r2 = 0;
int r = 0;
if (i == -1) {
return 0;
}
if (j >= w[i]) // 背包剩余空间可以放下物品 i
{
r1 = Knap(i - 1, j - w[i]) + v[i]; // 第i件物品放入所能得到的价值
r2 = Knap(i - 1, j); // 第i件物品不放所能得到的价值
r = (r1 >= r2) ? r1 : r2;
if (r == r1)//记录数组x[]
x[i] = 1;
else
x[i] = 0;
}
return r;
}
}
备忘录:
public class KnapsackMemo {
private int[] v;
private int[] w;
private int[][] m;
private int capacity;
private int N;
public int x[];
/**
* 价值,重量,背包容量
* @param _v
* @param _w
* @param _c
*/
public KnapsackMemo(int[] _v, int[] _w, int _c) {
this.v = _v;
this.w = _w;
this.capacity = _c;
N = v.length;
m = new int[N][_c + 1];
x = new int[N];
initial();
}
public int KnapsackMemoMethod(int i, int c) {
if (m[i][c] != -1)
return m[i][c]; // 使用m[i][c]需要检查两个下标是否出界
int result = 0;
if (i == N - 1)
if (c >= w[i]) {
m[i][c] = v[i];
return v[i];
} else {
m[i][c] = 0;
return 0;
}
else {
if (c >= w[i]) {
int selected = KnapsackMemoMethod(i + 1, c - w[i]) + v[i];
int unselected = KnapsackMemoMethod(i + 1, c);
result = selected > unselected ? selected : unselected;
m[i][c] = result;
return result;
} else // c < w[i]
{
result = KnapsackMemoMethod(i + 1, c);
m[i][c] = result;
return result;
}
}
}
public void Trace(int i, int c) {
if (i == N - 1) {
if (m[i][c] == v[i])
x[i] = 1;
else {
x[i] = 0;
}
return;
} else {
if (m[i][c] == m[i + 1][c]) {
x[i] = 0;
Trace(i + 1, c);
} else {
x[i] = 1;
Trace(i + 1, c - w[i]);
}
}
}
public void initial()
{
for (int i = 0; i < N; i++)
{
for (int j = 0; j <= capacity; j++)
{
m[i][j] = -1;
}
}
}
}
动态规划:
自底向上的动态规划方法,数组[0]=0;不存放数据。
public static void knapsack(int[] v, int[] w, int c, int[][] m) {
int n = v.length - 1;
int jMax = Math.min(w[n] - 1, c);
for (int j = 0; j <= jMax; j++)
m[n][j] = 0;
for (int j = w[n]; j <= c; j++)
m[n][j] = v[n];
for (int i = n - 1; i >= 1; i--) {
jMax = Math.min(w[i] - 1, c);
for (int j = 0; j <= jMax; j++)
m[i][j] = m[i + 1][j];
for (int j = w[i]; j <= c; j++)
m[i][j] = Math.max(m[i + 1][j], m[i + 1][j - w[i]] + v[i]);
m[0][c] = m[1][c];
if (c >= w[0])
m[0][c] = Math.max(m[0][c], m[1][c - w[0]] + v[0]);
}
}
/*
*
*/
public static void traceback(int[][] m, int[] w, int c, int[] x) {
int n = w.length - 1;
for (int i = 0; i < n; i++)
if (m[i][c] == m[i + 1][c])
x[i] = 0;
else {
x[i] = 1;
c -= w[i];
}
x[n] = (m[n][c] > 0 ? 1 : 0);
}
}
测试代码:(背包中物品重量和价值是随机数产生。数组第一个数w[0]不存放数据,而_w[0]存放数据。)
public class test {
static int num = 0;// 物品数
static int vol = 0;// 背包容量
static int c;// 背包容量
static int[] x;// 数组中用0、1表示物品是否装进背包
static int[] w;// 每个物品重量
static int[] v;// 每个物品价值
static int[] _w;// 副本
static int[] _v;
public static void main(String sg[]) {
Scanner s = new Scanner(System.in);
System.out.println("输入数据量:");
num = s.nextInt();
System.out.println("输入背包容量:");
vol = s.nextInt();
s.close();
// 数组初始化
c = vol;
x = new int[num + 1];
w = new int[num + 1];
v = new int[num + 1];
_w = new int[num];
_v = new int[num];
// 生成物品重量和价值
randomnumbers();
// 输出产生的重量和价值的数据
System.out.println("物品的重量和价值:");
for (int i = 1; i <= num; i++) {
System.out.print(w[i] + " ");
}
System.out.println();
for (int i = 1; i <= num; i++) {
System.out.print(v[i] + " ");
}
System.out.println("\n\r");
// 分别调用测试函数
knapbackTest();
//
System.out.println();
MemoTest();
//
System.out.println();
RecursionTest();
}
/**
* 递归方法测试函数
*/
private static void RecursionTest() {
System.out.println("递归方法:");
KnapsackRecursion kr = new KnapsackRecursion(_v, _w, c);
long startTime = System.currentTimeMillis();
int max = kr.Knap(num - 1, c);
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
// 打印x[]数组中的01数据
System.out.println("数组x[]:");
for (int i = 0; i < num; i++) {
System.out.print(kr.x[i] + " ");
}
System.out.println("\n" + "装入物品序号:");
GetNumBy(2, kr.x);
System.out.println("time:" + time + "ms");
System.out.println("max:" + max);
}
/**
* 备忘录方法测试函数
*/
private static void MemoTest() {
System.out.println("动态规划(备忘录):");
KnapsackMemo km = new KnapsackMemo(_v, _w, c);
// time
long startTime = System.currentTimeMillis();
int max = km.KnapsackMemoMethod(0, c);
long endTime = System.currentTimeMillis();
long time = endTime - startTime;// 计算时间
// 计算出x[]
km.Trace(0, c);
// 打印x[]数组中的01数据
System.out.println("数组x[]:");
for (int i = 0; i < num; i++) {
System.out.print(km.x[i] + " ");
}
System.out.println("\n" + "装入物品序号:");
GetNumBy(2, km.x);
// time
System.out.println("time:" + time + "ms");
// 最优解
System.out.println("max:" + max);
System.out
.println("----------------------------------------------------------");
}
/**
* 自底向上动态规划方法测试函数
*/
private static void knapbackTest() {
System.out.println("自底向上动态规划:");
int max = maxM(w, c);// 获取j的最大值,从而通过new关键字创建m数组,在内存中为数组分配相应的存储空间
int[][] m = new int[v.length][max + 1];
// 计算和时间
long startTime = System.currentTimeMillis();
Knapsack.knapsack(v, w, c, m);
long endTime = System.currentTimeMillis();
long time = endTime - startTime;// 计算时间
Knapsack.traceback(m, w, c, x);
// 打印矩阵
/*
* System.out.println("矩阵:"); for (int i = 1; i <= num; i++) { for (int
* k = 0; k < c; k++) System.out.print(m[i][k] + " ");
* System.out.println(); }
*/
// 打印01
String _x = new String();
System.out.println("数组x[]:");
for (int i = 1; i <= num; i++) {
_x += x[i] + " ";
}
System.out.println(_x);
// 装入号码:
System.out.println("装入物品序号:");
GetNumBy(1, x);
// 打印动态规划所需时间
System.out.println("time:" + time + "ms");
// 最大值
System.out.println("max:" + m[1][c]);
System.out
.println("----------------------------------------------------------");
}
/**
*
* @param w
* @param c
* @return
*/
public static int maxM(int[] w, int c) {
int max = c;
for (int i = 1; i < w.length; i++) {
if (w[i] > c)
max = w[i];
}
return max;
}
/**
* 产生随机数据的函数(物品的重量和价值)
*/
public static void randomnumbers() {
// 产生随机数装入数组
ArrayList<Integer> array_v = new ArrayList<>();
ArrayList<Integer> array_w = new ArrayList<>();
array_v.add(0);// v[0]不存数值
array_w.add(0);
for (int i = 0; i < num; i++)
array_w.add((int) (Math.random() * 20));
for (int j = 0; j < num; j++)
array_v.add((int) (Math.random() * 100));
int j = 0;
for (Integer i : array_w) {
w[j] = (int) i;
j++;
}
j = 0;
for (Integer i : array_v) {
v[j] = (int) i;
j++;
}
// 数据副本
array_w.remove(0);
array_v.remove(0);
j = 0;
for (Integer i : array_w) {
_w[j] = (int) i;
j++;
}
j = 0;
for (Integer i : array_v) {
_v[j] = (int) i;
j++;
}
}
/**
*
* @param type
* @param x
* type为1是指自底向上的动态规划,2是指备忘录法
*/
public static void GetNumBy(int type, int[] x) {
if (type == 1) {
for (int i = 0; i <= num; i++) {
if (x[i] == 1)
System.out.print(i + " ");
}
System.out.println();
} else if (type == 2) {
for (int i = 0; i < num; i++) {
if (x[i] == 1)
System.out.print((i + 1) + " ");
}
System.out.println();
}
}
}
(递归方法不正确)