动态规划-背包问题

1、0/1背包问题:在N件物品取出若干件放在容量为W的背包里,每件物品的体积为W1,W2……Wn(Wi为整数),与之相对应的价值为P1,P2……Pn(Pi为整数)。求背包能够容纳的最大价值。

递推公式:F(i, j) = Max{ F(i-1, j)+ F(i-1, j-wi)+pi  }

原理分析:考虑第i件物品,假设不加入,最大值则为放入i-1件时的最大值;假设加入,最大值则为当前值+剩余空间的最大值,最终两者情况相比取其大。子问题也遵循相同的原理。

2、代码实现:

  (1)递归实现:

   

import java.util.Scanner;

/**
 * 
 * @author 
 * 0/1背包问题
 */
public class Main9 {
	public static void main(String args[]) {
		Scanner s = new Scanner(System.in);
		/**
		 * 定义测试数据
		 */
		int w[] = {1,5,2,3,4};
		int v[] = {5,7,2,6,100};
		int len = w.length;
		int topW = 8;
		int topV = findMaxVal(w,v,len-1,topW);
		System.out.println(topV);
		s.close();
		
	}
	public static int findMaxVal(int[] w,int[] v,int cur_index,int cur_topw) {
		/*
		 * 递归结束条件:没有物品可取
		 */
		if(cur_index<0) {
			return 0;
		}
		
		//假设不取第i件物品
		int lastMax = findMaxVal(w, v,cur_index-1, cur_topw);
		int curMax = lastMax;
		if(w[cur_index]<=cur_topw) {
			//假设取第i件物品
			curMax = Math.max(lastMax, findMaxVal(w,v,cur_index-1,(cur_topw-w[cur_index]))+v[cur_index]);
		}
		return curMax;
	}
}

  (2)递归遍历+记忆搜索:当第一次计算时记录值。缩小遍历次数。

import java.util.Scanner;

public class Main9_2 {
	public static void main(String args[]) {
		Scanner s = new Scanner(System.in);
		/**
		 * 定义测试数据
		 */
		int w[] = {1,5,2,3,4};
		int v[] = {5,7,2,6,1};
		int len = w.length;
		int topW = 8;
		int keep[][] = new int[len][topW+1];
		//先将记录数组初始化为-1.
		for(int i=0;i<len;i++) {
			for(int j=0;j<=topW;j++) {
				keep[i][j] = -1;
			}
		}
		int topV = findMaxVal(w,v,len-1,topW,keep);
		System.out.println(topV);
		s.close();
		
	}
	public static int findMaxVal(int[] w,int[] v,int cur_index,int cur_topw,int keep[][]) {
		/*
		 * 递归结束条件:选择到最后一件物品
		 */
		if(cur_index==0) {
			int res = w[0]<=cur_topw?v[0]:0;
			return res;
		}
		//假设不取第i件物品,查询子问题是否被记录
		int lastMax= keep[cur_index-1][cur_topw]==-1?
				findMaxVal(w, v,cur_index-1, cur_topw,keep):keep[cur_index-1][cur_topw];
		
		int curMax = lastMax;
		if(w[cur_index]<=cur_topw) {
			//假设取第i件物品,递归之前查询是否被记录。
			int temp = keep[cur_index-1][cur_topw-w[cur_index]]==-1?findMaxVal(w,v,cur_index-1,(cur_topw-w[cur_index]),keep):
				keep[cur_index-1][cur_topw-w[cur_index]];
			curMax = Math.max(lastMax,temp+v[cur_index]);
		}
		return curMax;
	}
}

  (3)多重循环实现:使用二维数组记录结果

import java.util.Scanner;

public class Main9_3 {
	public static void main(String args[]) {
		Scanner s = new Scanner(System.in);
		//定义测试数据
		int w[] = {1,5,2,3,4};
		int v[] = {5,7,2,6,1};
		int len = w.length;
		int topW = 8;
		int keep[][] = new int[len][topW+1];
		for(int i=0;i<=topW;i++) {
			keep[0][i] = w[0]<=i?v[0]:0;
		}
		//开始填表(二维数组)
		for(int i=1;i<len;i++) {
			for(int j=0;j<=topW;j++) {
				keep[i][j] = keep[i-1][j];
				if(w[i]<=j) {
					keep[i][j] = Math.max(keep[i][j], keep[i-1][j-w[i]]+v[i]);
				}
			}
		}
		//打印填表结果,实际可省略
		for(int i=0;i<len;i++) {
			for(int j=0;j<=topW;j++) {
				System.out.print(keep[i][j]+" ");
			}
			System.out.println();
		}
		System.out.println(keep[len-1][topW]);
		s.close();
	}

}

  (4)多重循环实现:使用一维循环数组记录当前结果。

    》要点:记录当前值时,需要从后往前更新(这也是循环的体现)。

 

package yrc1;

import java.util.Scanner;

public class Main9_4 {
	public static void main(String args[]) {
		//最后结果:0 5 5 7 11 11 13 13 14 
		Scanner s = new Scanner(System.in);
		//定义测试数据
		int w[] = {1,5,2,3,4};
		int v[] = {5,7,2,6,1};
		int len = w.length;
		int topW = 8;
		int keep[] = new int[topW+1];
		for(int i=0;i<=topW;i++) {
			keep[i] = w[0]<=i?v[0]:0;
		}
		for(int i=1;i<len;i++) {
			for(int j=topW;j>=w[i];j--) {
				keep[j] = Math.max(keep[j], keep[j-w[i]]+v[i]);
			}
		}
		//显示最后记录结果
		for(int i=0;i<=topW;i++) {
			System.out.print(keep[i]+" ");
		}
		System.out.println("
"+keep[topW]);
	}

}

  

  

  

原文地址:https://www.cnblogs.com/dream-flying/p/12567109.html