zoukankan      html  css  js  c++  java
  • 动态规划-背包问题

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

      

      

      

  • 相关阅读:
    知乎 : 有什么你认为很简单的问题实际的证明却很复杂?
    民科吧 的 一题 : ∂ f / ∂ x =
    知乎: Lurie 的 derived algebraic geometry 有多重要?
    说说 网友 专业证伪 的 那道 几何题
    在 《数学问题,连接两个点的曲线旋转所成曲面中,面积最小的曲线是什么?》 里 的 讨论
    证明 夹逼定理 和 洛必达法则
    一道数学题 : 数列 { bn } 收敛, 证明 { an } 也收敛
    一道数学题 : f ( 2^x ) + f ( 3^x ) = x , 求 f ( x )
    网友 lzmsunny96 的 一个 分数 分解题
    我 搞了一个 尺规作图 不能 实现 三等分角 的 证明
  • 原文地址:https://www.cnblogs.com/dream-flying/p/12567109.html
Copyright © 2011-2022 走看看