动态规划算法

求数组中不相邻的最大值

解决方案,假设opt数组为最优解,比如opt[6]就表示arr数组中下标0到6这段的最优解

即opt[n]=Math.max(opt[n-1],opt[n-2]+arr[n])

上诉公式表示 不取下标为n的选项和取下标为n的选项两种方案的最大值

边界为 opt[0]=arr.get(0) opt[1]=Math.max(arr.get(0),arr.get(1)),还是比较好理解的。

/**
	 * 获取数组arr中不相邻的数字相加最大值
	 * @param arr
	 * @return
	 */
	private static Integer getMaxValue(List<Integer> arr){
		Integer[] opt=new Integer[arr.size()];
		opt[0] = arr.get(0);
		opt[1] = Math.max(arr.get(0), arr.get(1));
		for(int i=2;i<arr.size();i++){
			Integer a = arr.get(i) + opt[i - 2];
			Integer b = opt[i - 1];
			opt[i] = Math.max(a, b);
		}
		System.out.println(JSON.toJSONString(opt));
		return opt[arr.size() - 1];
	}

求数组中是否存在不相邻的选项和为某值

递归方案:

/**
	 * 递归方式求list数组中,是否存在不相邻的数字和为result
	 * @param list
	 * @param result
	 * @return
	 */
	private static boolean containSubset(List<Integer> list,Integer length,Integer result){
	    if(result==0)return true;
		if(length.equals(1)){
			return list.get(0).equals(result);
		}else if(length.equals(2)){
			return list.get(0).equals(result) || list.get(1).equals(result);
		}

		if(list.get(length-1)>result){
			//不选
			return containSubset(list, length - 1, result);
		}
		boolean A = containSubset(list, length - 2, result - list.get(length - 1));
		boolean B = containSubset(list, length - 1, result);
		return A || B;
	}

动态规划方案:

	/**
	 * 动态规划 求list数组中不相邻的数字和是否可以为result()
	 * @param list
	 * @param result
	 * @return
	 */
	private static boolean dp_subset(List<Integer> list,Integer result){
		Boolean[][] arr = new Boolean[list.size()][result + 1];
        for(int i=0;i<list.size();i++){
			arr[i][0] = true;
		}
        for(int j=1;j<=result;j++){
			arr[0][j] = list.get(0).equals(j);
		}
        for(int j=1;j<=result;j++){
			arr[1][j] = arr[0][j] || list.get(1).equals(j);
		}

        for(int i=2;i<list.size();i++){
        	for(int j=1;j<result+1;j++){
               if(list.get(i)>j){
				   arr[i][j] = arr[i - 1][j];
			   }else{
				   boolean A = arr[i - 1][j];
				   boolean B = arr[i - 2][j - list.get(i)];
				   arr[i][j] = A || B;
			   }
			}
		}
		//showArr(arr);
		return arr[list.size() - 1][result];
	}

动态规划方案不太好理解,这里举例

list为5, 4, 3, 1, 6, 2, 7,result为12

       0     1     2     3     4     5     6     7     8     9     10    11   12
5    true false false false false  true false false false false false false false 
4    true false false false  true  true false false false false false false false 
3    true false false  true  true  true false false  true false false false false 
1    true  true false  true  true  true  true false  true false false false false 
6    true  true false  true  true  true  true false  true  true  true  true false 
2    true  true  true  true  true  true  true  true  true  true  true  true false 
7    true  true  true  true  true  true  true  true  true  true  true  true  true 

纵坐标为list数组中的具体值,横坐标为result

每个坐标的意思就是在子数组中是否存在不相邻的和为坐标值的组合

比如arr[2,4]表示5,4,3三个子集中是否存在不相邻的组合和为4

可以明星看出最右下角的二维数组值就是要的结果。

国王与金矿(0-1背包问题)

这两个问题都差不多

	/**
	 *
	 * @param n 第几个金矿
	 * @param w 总共有几个人
	 * @param g 数组,存放每个金矿的黄金数
	 * @param p 数组,存放每个金矿需要的工人数
	 * @return
	 */
	public static int getMostGlodForDP2(int n, int w, int[] g, int[] p) {
		int[][] arr=new int[n][w+1];
		for(int i=0;i<n;i++){
			for(int j=0;j<w+1;j++){
				//j即为人数
				//第一座金矿
				if(i==0){
					if(j<p[0]){
						arr[i][j]=0;
					}else{
						arr[i][j] = g[i];
					}
				}else{
					if(j<p[i]){
						arr[i][j] = arr[i - 1][j];
					}else{
						//不采这个金矿收益
						int a=arr[i-1][j];
						//采集这个金矿收益(金矿收益+剩余人数的最大收益)
						int b = g[i] + arr[i - 1][j - p[i]];
						arr[i][j] = Math.max(a, b);
					}
				}
			}
		}
		showArr(arr);
		return arr[n - 1][w - 1];
	}

	/**
	 * 打印二维数组
	 * @param arr
	 */
	private static void showArr(int[][] arr){
		for(int i=0;i<arr.length;i++){
			System.out.println("");
			for(int j=0;j<arr[i].length;j++){
				System.out.print(StringUtils.center(String.valueOf(arr[i][j]), 5, " "));
			}
		}
	}

输出:

		int[] g = { 400, 500, 200, 300, 350 };
		int[] p = { 5, 5, 3, 4, 3 };
		getMostGlodForDP2(5, 10, g, p);
		
  0    0    0    0    0   400  400  400  400  400  400 
  0    0    0    0    0   500  500  500  500  500  900 
  0    0    0   200  200  500  500  500  700  700  900 
  0    0    0   200  300  500  500  500  700  800  900 
  0    0    0   350  350  500  550  650  850  850  900 

横向为人数(横向第一列表示人数0,1个人,2个人,3个人。。。),纵向为金矿

可以一目了然的看出每添加一座金矿,10个人的收益情况

上诉代码最难理解的就是这块int b = g[i] + arr[i - 1][j - p[i]];

代码的意思就是,采集这块金矿收益+剩余人数采集其他金矿的最大收益

其实这里只要其他金矿收益,只要两个一维数组就可以了,这里方便

最长回文子串

	/**
	 * 动态规划方式
	 * @param s
	 * @return
	 */
	private static String myTestCode(String s){
		int len = s.length();
		if(len<2)return s;
		char[] arr = s.toCharArray();
		int maxLen=1;
		int begin=0;
		boolean[][] dp = new boolean[len][len];
		for(int j=1;j<len;j++){
			for(int i=0;i<j;i++){
				if(arr[i]!=arr[j]){
					dp[i][j]=false;
				}else{
					if(j-i>2){
						dp[i][j] = dp[i + 1][j - 1];
					}else{
						dp[i][j]=true;
					}
				}
				if(dp[i][j]&&(j-i+1)>maxLen){
					begin=i;
					maxLen = j - i + 1;
				}
			}
		}
		return s.substring(begin, begin + maxLen);
	}

参考

漫画:什么是动态规划?

经典动态规划:0-1 背包问题

看动画轻松理解「递归」与「动态规划」

动态规划解题套路框架

算法-动态规划 Dynamic Programming--从菜鸟到老鸟

浅谈我对动态规划的一点理解---大家准备好小板凳,我要开始吹牛皮了~~~

动态规划(第2讲)视频

posted @ 2020-08-25 23:14  hongdada  阅读(449)  评论(0编辑  收藏  举报