回溯算法——背包问题

/**
 * 有一个背包,背包总的承载重量是Wkg。现在我们有n个物品,每个物品的重量不等,并且不可分割。
 * 我们现在期望选择几件物品,装载到背包中。在不超过背包所能装载重量的前提下,如何让背包中物品的总重量最大。
 * @author Administrator
 *
 */
public class HuiSuSuanFa {
	public static void main(String[] args) {
		int[] items = new int[]{11,9,7,6,5,};
		f(0,0,items,5,20);
		System.out.println("maxW="+maxW);
		
		System.out.println(Knapsack(new int[]{2,2,4,6,3},5,9));
	}
	
	/**
	 * 背包总的承载重量是Wkg。有n个物品,每个物品的重量不等,并且不可分割。我们现在期望选择几件物品,
	 * 装载到背包中。在不超过背包所能装载重量的前提下,如何让背包中物品的总重量最大。
	 * 
	 * 假设背包可承受重量20,物品个数5,物品重量存储在数组a中,那可以这样调用函数:
	 * f(0, 0, a, 5, 20)
	 * 
	 * @param i 表示考察到哪个物品了
	 * @param cw 表示当前已经装进去的物品的重量和
	 * @param items 表示每个物品的重量
	 * @param n 表示物品个数
	 * @param w 背包重量
	 */
	public static int maxW = Integer.MIN_VALUE;
	public static void f(int i,int cw,int[] items,int n,int w) {
		if(cw==w||i==n) {//cw==w表示装满了;i==n表示已经考察完所有的物品
			System.out.println("cw==w||i==n cw="+cw);
			if(cw>maxW) maxW = cw;
			return;
		}	
		System.out.println("不放入"+(i+1)+",cw="+cw);
		f(i+1,cw,items,n,w);//选择不装第i个数
		if(cw+items[i]<=w) {//已经超过可以背包承受的重量的时候,就不要再装了
			System.out.println("放入"+(i+1)+",cw="+(cw+items[i]));
			f(i+1,cw+items[i],items,n,w);//选择装第i个数
		}
	}
	
	/**
	 * 动态规划:背包总的承载重量是Wkg。有n个物品,每个物品的重量不等,并且不可分割。我们现在期望选择几件物品,
	 * 装载到背包中。在不超过背包所能装载重量的前提下,如何让背包中物品的总重量最大。
	 * @param weight 物品重量 {2,2,4,6,3}
	 * @param n 物品个数
	 * @param w 背包可承载重量
	 * @return
	 */
	public static int Knapsack(int[] weight,int n,int w) {
		boolean [][] states = new boolean[n][w+1];//默认值false
		states[0][0] = true;//第一行的数据要特殊处理,可以利用哨兵优化
		states[0][weight[0]] = true;
		
		for(int i=1;i<n;++i) {//动态规划状态转移
			for(int j=0;j<=w;++j) {//不把第i个物品放入背包
				if(states[i-1][j]==true) {
					states[i][j] = states[i-1][j];
				}
			}
			for(int j=0;j<=w-weight[i];++j) {//把第i个物品放入背包
				if(states[i-1][j]==true) {
					states[i][j+weight[i]]=true;
				}
			}
		}
		
		for(int i=w;i>=0;--i) {//输出结果
			if(states[n-1][i]==true) return i;
		}
		
		return 0;
	}
	
	/**
	 * 优化Knapsack:背包总的承载重量是Wkg。有n个物品,每个物品的重量不等,并且不可分割。我们现在期望选择几件物品,
	 * 装载到背包中。在不超过背包所能装载重量的前提下,如何让背包中物品的总重量最大。
	 * 
	 * @param items 物品重量 {2,2,4,6,3}
	 * @param n 物品个数
	 * @param w 背包可承载重量
	 * @return
	 */
	public static int Knapssack2(int[] items,int n,int w) {
		boolean[] states = new boolean[w+1];//默认值false
		states[0] = true;//第一行的数据要特殊处理,可以利用哨兵优化
		states[items[0]]=true;
		for(int i=1;i<n;i++) {
			for(int j=w-items[i];j>=0;--j) {
				if(states[j]==true) states[j+items[i]] = true;
			}
		}
		
		for(int i=w;i>=0;--i) {//输出结果
			if(states[i]==true) return i;
		}
		
		return 0;
	}
	
	/**
	 * 动态规划:重量一定,放入价值最大
	 * 对于一组不同重量、不同价值、不可分割的物品,我们选择将某些物品装入背包,在满足背包最大重量限制的前提下,
	 * 背包中可装入物品的总价值最大是多少呢?
	 * @param weight 物品重量 {2,2,4,6,3}
	 * @param value 物品价值
	 * @param n 物品个数
	 * @param w 背包可承载重量 
	 * @return
	 */
	public static int knapsack3(int[] weight,int[] value,int n,int w) {
		int[][] states = new int[n][w+1];
		for(int i=0;i<n;i++) {//初始化states
			for(int j=0;j<w+1;++j) {
				states[i][j] = -1;
			}
		}
		
		states[0][0] = 0;
		states[0][weight[0]] = value[0];
		for(int i=1;i<n;i++) {//动态规划,状态转移
			for(int j=0;j<=w;j++) {//不选择第i个物品
				if(states[i-1][j]>=0) {
					states[i][j]=states[i-1][j];
				}
			}
			for(int j=0;j<=w-weight[i];j++) {
				int v = states[i-1][j]+value[j];
				if(v>states[i][j+weight[i]]) {
					states[i][j+weight[i]] = v;
				}
			}
		}
		
		//找出最大值
		int maxvalue = -1;
		for(int j=0;j<=w;j++) {
			if(states[n-1][j]>maxvalue) maxvalue = states[n-1][j];
		}
		
		return maxvalue;
	}
	
	/**
	 * 动态规划:找出最合适的满减
	 * 
	 * @param items 商品价格
	 * @param n 商品个数
	 * @param w 表示满减条件,比如200
	 */
	public static void doublelladvance(int[] items,int n,int w) {
		boolean[][] states = new boolean[n][3*w+1];//超过三倍就没有薅羊毛的价值了
		states[0][0] = true;//第一行的数据要特殊处理
		states[0][items[0]] = true;
		for(int i=0;i<n;i++) {//动态规划
			for(int j=0;j<=3*w;j++) {//不购买第i个商品
				if(states[i-1][j]==true) states[i][j] = states[i-1][j];
			}
			for(int j=0;j<=(3*w-items[i]);j++) {
				if(states[i-1][j]==true) states[i][j+items[i]]=true;
			}
		}
		
		int j;
		for(j=w;j<3*w+1;j++) {
			if(states[n-1][j]==true) break;//输出结果大于等于w的最小值
		}
		
		if(j==-1) return;//没有可行解
		for(int i=n-1;i>=1;--i) {//i表示二维数组中的行,j表示列
			if(j-items[i]>=0 && states[i-1][j-items[i]]==true) {
				System.out.println(items[i]+" ");//购买这个商品
				j = j -items[i];
			}//else 没有购买这个商品,j不变
		}
		if(j != 0) System.out.println(items[0]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值