动态规划——背包问题

引入案例

概念

  • 动态规划 状态转移方程(递推式 )
  • 多阶段决策问题:动态规划常常用于求解多阶段决策问题
  • 动态规划求解的过程依赖于上一步的求解过程,这是与分治法最大的区别,他们的本质还是大问题化为小问题。

背包问题

背包问题 可以分为 01 背包完全背包下面介绍01 背包

  • 有一个背包,容量为4磅 , 现有如下物品

在这里插入图片描述

  • 要求达到的目标为装入的背包的总价值最大,并且重量不超出
  • 要求装入的物品不能重复

核心思路

参数说明

  • v[i]、w[i]分别为第i个物品的价值和重量,C为背包的容量。
  • v[i][j]表示在前i个物品中能够装入容量为j的背包中的最大价值(就是横纵坐标交错值)

实施步骤

  • v[i][0]=v[0][j]=0 表示 填入表 第一行和第一列是0
  • w[i]> j 时: --> 准备加入新增的商品的容量大于 当前背包的容量
    v[i][j]=v[i-1][j] ,就直接使用上一个单元格的装入策略
  • j>=w[i]时–> 准备加入的新增的商品的容量小于等于当前背包的容量
    v[i][j]=max{v[i-1][j], v[i]+v[i-1][j-w[i]]}
    装入的方式:
  • v[i-1][j]: 就是上一个单元格的装入的最大值
  • v[i] : 表示当前商品的价值
  • v[i-1][j-w[i]] : 装入i-1商品(装之前的商品),到剩余空间j-w[i]的最大值

图解

在这里插入图片描述

填装过程参考链接

代码

上述问题 求解代码

package Algorithm;



/**
 * @author LZH.create
 *    分治法  --> 动态规划    
 */
public class KnapaskProblem {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		 int [] w = {1,4,3} ;  // 三件商品的质量
		 
		 int[] val = {1500, 3000, 2000}; //物品的价值 这里val[i] 就是前面的v[i]
			int m = 4; //背包的容量
			int n = val.length; //物品的个数
			
			
			
			
			//v[i][j] 表示在前i个物品中能够装入容量为j的背包中的最大价值
			int[][] v = new int[n+1][m+1] ;    // 其中的 1 为 0 行
 			
			
			//为了记录放入商品的情况,我们定一个二维数组
			int[][] path = new int[n+1][m+1];
			
			//初始化第一行和第一列, 这里在本程序中,可以不去处理,因为默认就是0
			for(int i = 0; i < v.length; i++) {
				v[i][0] = 0; 
			}
			for(int i=0; i < v[0].length; i++) {
				v[0][i] = 0; 
			}
			

			// 动态规划 
			for(int i = 1; i < v.length; i++) {  
				for(int j=1; j < v[0].length; j++) { 
					//公式
					if(w[i-1]> j) { // 因为我们程序i 是从1开始的,因此原来公式中的 w[i] 修改成 w[i-1]
						v[i][j]=v[i-1][j];
					} else {
				
						if(v[i - 1][j] < val[i - 1] + v[i - 1][j - w[i - 1]]) {
							v[i][j] = val[i - 1] + v[i - 1][j - w[i - 1]];
							//把当前的情况记录到path
							path[i][j] = 1;     // 把放过东西的记录
						} else {
							v[i][j] = v[i - 1][j];
						}
						
					}
				}
			}
			
			//输出一下v 看看目前的情况
			for(int i =0; i < v.length;i++) {
				for(int j = 0; j < v[0].length;j++) {
					System.out.print(v[i][j] + " ");
				}
				System.out.println();
			}
			
			System.out.println("============================");

		
			int i = path.length - 1; //行的最大下标
			int j = path[0].length - 1;  //列的最大下标
			
			while(i > 0 && j > 0 ) { //从path的最后开始找
				if(path[i][j] == 1) {
					System.out.printf("第%d个商品放入到背包\n", i); 
					j -= w[i-1]; //w[i-1]     从后面开始  调整背包容量
				}
				i--;
			}
			
	}

}

正整数的摆动序列 (蓝桥杯)

  • 第十一届蓝桥杯模拟赛

问题描述

  • 如果一个序列的奇数项都比前一项大,偶数项都比前一项小,则称为一个摆动序列。即 a[2i]<a[2i-1], a[2i+1]>a[2i]

  • 小明想知道,长度为 m,每个数都是 1 到 n 之间的正整数的摆动序列一共有多少个。

  • 输入格式 :输入一行包含两个整数 m,n。

  • 输出格式 : 输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。

  • 样例输入 3 4

  • 样例输出 14

样例说明

以下是符合要求的摆动序列:
  2 1 2
  2 1 3
  2 1 4
  3 1 2
  3 1 3
  3 1 4
  3 2 3
  3 2 4
  4 1 2
  4 1 3
  4 1 4
  4 2 3
  4 2 4
  4 3 4

评测用例规模与约定

对于 20% 的评测用例,1 <= n, m <= 5;
  对于 50% 的评测用例,1 <= n, m <= 10;
  对于 80% 的评测用例,1 <= n, m <= 100;
  对于所有评测用例,1 <= n, m <= 1000。

题解

参考亓大佬的解题思路 👍

跳转链接

首先贴下根据亓大佬的解法改编的 Java 代码

package SimulationGame11;



import java.util.Scanner;

/**
 * @author LZH.create
 *  Date : 2021.4.12    动态规划永远的神!
 */
public class wigglingInteger {

	public static void main(String [] args) {
		Scanner sc = new Scanner(System.in) ;
		
		int m = sc.nextInt() ;   // 数的长度(几位数)
		int n = sc.nextInt() ;   // 数的可选范围
		
		int [][] dp = new int[1024][1024]  ;
		for(int i = 1 ; i<= n ;i++) {
			dp[1][i] = 1 ;  // 第一位 数可选的为 1
 		}
		
	    for(int i = 2 ; i <= m  ;i++) {
	    	for(int j = 1 ; j <= n ; j++) {
	    		if((i&1) == 1) {  // 异或运算  判断奇偶 
	    			int temp = 0 ;
	    			for(int k = 1; k <= j-1 ;k++ ) {  // 第i-1位时选择,数为1到j-1的和
	    				temp =  (temp + dp[i-1][k]) % 10000 ;   // 本来可以运用  += 
	    			}
	    			dp[i][j] = temp ;
	    		} else {    // 偶数
	    			int temp = 0 ;
	    			for(int k = j + 1 ; k <= n ;k++) { //第i-1位时,数为j+1到n的和
	    				temp = (temp + dp[i-1][k]) %10000 ;
	    			}
	    			dp[i][j] = temp ;
	    		}
	    	}
	    }
	    
	    // 输出结果  结果在最后一行  (即为第m 位数有多少结果)
	    int ans = 0 ;
	    for(int i = 0 ; i <= n ;i++) {
	    	ans += dp[m][i] ;
	    }
	    
	    System.out.println(ans);
	    
	    
	}

}

滚动数组

动态规划怎么能少了 滚动数组思想呢?

详见下回分享哈~

推荐

这位北大的大佬讲解的不错 👉跳转链接

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值