01背包问题动态规划

递归、备忘录、自底向上动态规划,计算时间依次减少。

递归方式:

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();
		}
	}

}
(递归方法不正确)




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值