【数据结构和算法】动态规划(参考灯神)【Java实现】

前言

首先本文参考灯神视频总结,配合视频食用效果更佳哦!动态规划

 

一、背景

首先了解斐波那契数列,通过斐波那契数列引入动态规划问题,由于递归存在重叠子问题,导致时间复杂度很大,可不可以考虑从递归的条件网上“递归”呢?这样就能够使用之前的条件,达到减少运算的效果,这就是动态规划。(纯个人理解)

 

二、例题

1.问题:一个人可以选择做下面8个任务,红字代表任务的工资,横轴表示时间段,一个人在一个时间段只能完成一个任务,完成才能离开做下一个。问怎样做任务在0~11时间段收入最高?

 

2.选和不选

由于0~11时间段,则8个任务都可以考虑,选择做第8个,和不选择做第8个;opt表示考虑做几个的最优解。

 

 

 prev(i)表示选择第i个后,可以选的上一个,比如prev(8)=5;prev(7)=3

通过以上的分析和计算,可以把问题转化为递归式,如下图所示:

  从opt(1)开始往下开始计算可得结果如下图所示:

 

三、动态规划实战

1.在这串数字当中选出一堆数字,要满足以下条件:

 

a.两个数不能相邻;

b.可以选多个数字,但每个数只能选一次;

参考上面的选和不选方法:

代码实现: 

(1)递归实现

package com.feng.dynamicplanning;

public class DynamicPlanning {
	
	//i表示
	public static int rec_opt(int[] arr, int i) {
		if(0 == i) {
			return 1;
		} else if(1 == i) {
			return 2;
		} else {
			return Math.max(rec_opt(arr, i-2) + arr[i], rec_opt(arr, i-1));
		}
	}
	
	public static void main(String[] args) {
		int[] arr = new int[]{1,2,4,1,7,8,3};
		System.out.println(rec_opt(arr, 6));
	}
}

 时间复杂度:2^n(因为涉及到了重叠子问题) 

(2)非递归实现

public static int dp_opt(int[] arr) {
		int[] opt = new int[arr.length];
		opt[0] = 1;
		opt[1] = Math.max(arr[0], arr[1]);
		for(int i = 2; i < arr.length; i++) {
			opt[i] = Math.max(opt[i-2] + arr[i], opt[i-1]);
		}
		return opt[arr.length-1];
	}

 

2.

 给定一个数组,找出和为9的方案。  可以选 多个数字,但每个数字最多只能选一次。

 

递归写出,肯定要想清楚递归出口情况:

(1)已经处理到一半的时候,已经找到了这种组合了

(2)到递归到最后一个,看最后一个是否等于S;

(3)如果出现arr[i]>S则只考虑不选的情况; 

递归式子为:

代码如下:

递归实现:

public static boolean rec_subset(int[] arr, int i, int s) {
		if(0 == s) {
			return true;
		} else if(0 == i) {
			return arr[0] == s;
		} else if(arr[i] > s) {
			return rec_subset(arr, i-1, s);
		} else {
			return rec_subset(arr, i-1, s-arr[i]) || rec_subset(arr, i-1, s);
		}
	}

 非递归实现:

public static boolean dp_subset(int[] arr, int S) {
		boolean[][] subset = new boolean[arr.length][S+1];
		//由左到右
		for(int s = 0; s < S+1; s++) {
			subset[0][s] = false;
		}
		if(S >= arr[0]) {
			subset[0][arr[0]] = true;
		}		
		//由上到下
		for(int i = 0; i < arr.length; i++) {
			subset[i][0] = true;
		}
		
		for(int i = 1; i < arr.length; i++) {//行
			for(int s = 1; s < S+1; s++) {//列
				if(arr[i] > s) {
					subset[i][s] = subset[i-1][s];
				} else {
					boolean A = subset[i-1][s-arr[i]];
					boolean B = subset[i-1][s];
					subset[i][s] = A || B;
				}		
			}
		}
		return subset[arr.length-1][S];
	}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值