动态规划问题小结

1.爬楼梯问题,到达楼梯的第i阶有多少中爬法

关键:第i阶楼梯,只可能从楼梯第i-1阶与i-2阶到达,所以到达第i阶的爬法与第i-1阶、第i-2阶的爬法直接相关。

这类问题分为四步:

1)原问题与子问题:找到子问题

2)第i个状态即为i阶台阶的所有走法数量

3)确认边界状态的值,边界状态为1阶台阶有一种走法,2阶有两种走法,即dp[1]=1,d[2]=2

4)确定状态转移方程dp[n] = dp[i-1] + dp[i-2]

代码实现:

public class Solution {
    public int JumpFloor(int target) {
        if(target<0)
            return -1;
        int[] dp = new int[target+2];    //每个数组里存的是有多少种方法
        dp[1] = 1;
        dp[2] = 2;
        for(int i=3;i<=target;i++){
            dp[i] = dp[i-1] + dp[i-2];
        }
        return dp[target];
    }
}

扩展:如果青蛙一次可以跳一阶,两阶,三阶,求到n阶台阶的跳法:

dp[n] = dp[n-1] + dp[n-2] + dp[n-3]

2.打家劫舍问题

一条直线上,有n个房屋,每个房屋中有数量不等的财宝,盗贼从房屋盗取财宝,如果从相邻的两个房屋盗取财宝就会触发报警器,问在不触发报警器的前提下,最多可以获取多少财宝。

记忆化搜索:


首先偷取了数组下标为0的房子,此时下一个房子就是从[2,n-1](因为不能偷取相邻的),可以理解题目是求从下标为0出开始偷取的最大值,如果先偷取了下标为0,那么转变为求从下标为2出开始偷取的最大值(递归下去),同理如果偷取的是第1个房子,此时下一个房子的偷取范围[3,n-1].........

从图中可以看到重叠子问题,所以可以使用记忆化搜索的方式。

代码实现(记忆化搜索方式):

class JianZhiOffer{
	static int[] memo;    //memo[i]表示抢劫nums[i...n]所能获得的最大收益。
	public static void main(String[] args) {
		int[] a = {5,2,6,3,1,7};
		memo = new int[a.length];
		Arrays.fill(memo,-1);
		System.out.println(tryRob(a,0));
	}
	public static int tryRob(int[] nums,int index){
		if(index>=nums.length)	//这个条件???
			return 0;
		if(memo[index]!=-1)
			return memo[index];
		int temp = -1;
		for(int i=index;i<nums.length;i++){
			if(nums[i]+tryRob(nums,i+2)>temp)
				temp = nums[i]+tryRob(nums,i+2);
			memo[index] = temp;
		}
		return temp;
	}
}
这里注意递归结束的条件:
if(index>=nums.length)	//这个条件???
	return 0;

如果index = 3,此时temp = nums[3] + tryRob(nums,5);其实tryRob(nums,5) = 0

如果index = 4 ,此时temp = num[4] + tryRob(nums,6);其中tryRob(nums,6) = 0

memo[i]表示考虑抢劫nums[i...n]所能获得的最大收益

关键:选择第i个房间盗取财宝,就一定不能选择第i-1个房间盗取财宝,若不选择第i个房间盗取财宝,则相当于只考虑前i-1个房间盗取财宝。

与上题的思路有一点一样:上题n级台阶有两种情况到达,第一种是从n-1台阶,另一个中是从n-2级台阶。

这题,盗取第n个房间的财宝,有两种方式,一种是从n-1房间,另一种是从n-2个房间

状态转移方程:dp[i] = max(dp[i-1],dp[i-2]+nums[i]);

代码实现:

class DP{
	public static void main(String[] args){
		int[] a = {5,2,6,3,1,7};
		rob(a);
	}
	public static void rob(int nums[]){
		int[] dp = new int[nums.length];
		dp[0] = nums[0];
		dp[1] = Math.max(nums[0], nums[1]);
		for(int i=2;i<=nums.length-1;i++){
			dp[i] = Math.max(dp[i-2]+nums[i],dp[i-1]);
		}
		System.out.println(dp[nums.length-1]);
	}
}

3.最大字段和

给定一个数组,求这个数组的连续子数组中,最大的那一段的和例如:{14,-2,4,-3,5,7,2,-39,22},和最大子序列是{14,-2,4,-3,5,7,2},返回27。

思路:求n个数的数组的最大子序列的和,转为分别求以第1个、第2个......第n个数字结尾的最大自序列和,在找出n个结果中的最大值。

dp[i]代表以第i个数字结尾的最大子段和,求dp[i]与dp[i-1]的关系。

代码实现:

//数组的所有连续子段的最大和
class DP
{
	public static void main(String[] args)
	{
		int[] a = {14,-2,4,-3,5,7,2,-39,22};
		maxSum(a,a.length);		
	}
	public static void maxSum(int[] a,int n)
	{
		int[] dp = new int[a.length];
		dp[0] = a[0];
		
		
		for(int i=1;i<n;i++)
		{
			dp[i] = Math.max(dp[i-1]+a[i], a[i]); 
		}
		//System.out.println(Arrays.toString(dp));
		//取出dp数组中的最大值
		int max = Integer.MIN_VALUE;
		for(int i=0;i<dp.length;i++)
		{
			if(dp[i]>max)
				max = dp[i];
		}
		System.out.println(max);
	}
}

4.剑指Offer(第二版)面试题14:剪绳子

题目一:给你一根长度为n的绳子,请把绳子剪成m段 (m和n都是整数,n>1并且m>1)每段绳子的长度记为k[0],k[1],...,k[m].请问k[0]*k[1]*...*k[m]可能的最大乘积是多少?

例如,当绳子的长度为8时,我们把它剪成长度分别为2,3,3的三段,此时得到的最大乘积是18.

分析:定义函数dp(n)为把长度为n的绳子剪成若干段后各段程度乘积的最大值。在剪第一刀的时候,我们有n-1中选择,也就是剪出来的第一段绳子的可能长度分别为1,2,.....;n-1,对应的第二段绳子的长度为n-1,n-2......1。因此       dp(n)=max(dp(i)*dp(n-i)),其中0<i<n。我们按照从下到上的顺序计算,也就是说我们先得到dp[(2)、dp(3)...然后求得dp(n),其中dp(n)是dp(1)*dp(n-1),dp(2)*dp(n-2).....dp(n-1)*dp(1)中的最大值。举例子说明:n=4时,dp[4]=dp[1]*dp[3],表示将长度为4的绳子,剪成1,3两段后的乘积,同样dp[2] = dp[2]*dp[2],表示将长度为4的绳子,减成2,2两段后的乘积。

代码实现:

//剪绳子问题
class JianZhiOffer{
	public static void main(String[] args) {
		System.out.println(maxCutting(8));
	}
	public static int maxCutting(int length){
		if(length<=1)	//如果小于等于1,输入不合法
			return 0;
		if(length==2)	//可以分为1,1
			return 1;
		if(length==3)	//可以分为:1,2 1,1,1
			return 2;
		int[] dp = new int[length+1];            //dp[n]计为长度为n的绳子剪开后的最大乘积
		dp[0] = 0;
		dp[1] = 1;			
		dp[2] = 2;
		dp[3] = 3;                              //从dp[1]-dp[3]都包含了自己在内
	    for(int i=4;i<=length;i++){
	    	int max = 0;
	    	for(int j=1;j<=i/2;j++){
	    		int temp = dp[j]*dp[i-j];	//长度为i的绳子的剪开后的最大的绳子等于它被剪开后子绳子的最大长度
	    		if(temp>max)
	    			max = temp;
	    	}
	    	dp[i] = max;
	    }
		return dp[length];
	}
}

说明:从dp[1]到dp[3]都包含了自己在内,而从dp[4]开始是按照题意所求的值并且从4开始都是由1,2,3拼凑成的。

同样的问题有



5.给定一个m*n的方格,起始点为方格的最左上方,终点为方格的最右下方,一个机器人只能向下以及向右移动,需要求出机器人从起始点到终点一共有多少种不重复的路径。问题的输入为方格的长度m以及宽度n,输出为不同路径的数量。


https://blog.csdn.net/codekiller_/article/details/73008244https://blog.csdn.net/codekiller_/article/details/73008244

机器人只能经过方格上方或者方格左侧到达方格,dp[i][j] = dp[i-1][j] + dp[i][j-1],同时dp[0][j] = dp[i][0] = 0。

代码实现:

class DP{
	public static void main(String[] args) {
		int m = 3;
		int n = 6;
		int[][] dp = new int[m][n];
		for(int i=0;i<m;i++){
			for(int j=0;j<n;j++){
				if(i==0||j==0){
					dp[i][j] = 1;
				}
				else{
					dp[i][j] = dp[i-1][j] + dp[i][j-1];
				}
			}
		}
		for(int i=0;i<m;i++){
			for(int j=0;j<n;j++){
				System.out.print(dp[i][j]+" ");
			}
			System.out.println();
		}
		System.out.println(dp[m-1][n-1]);
	}
}

同种类型的题:


https://blog.csdn.net/weixin_38314447/article/details/79074795

代码实现(动态规划的方法):

class DP{
	public static void main(String[] args) {
		int m = 9;
		int n = 9;
		int[][] dp = new int[9][9];
		for(int i=0;i<9;i++){
			for(int j=0;j<9;j++){
				if(i==0||j==0){
					dp[i][j] = 1;
				}
				else{
					dp[i][j] = dp[i-1][j] + dp[i][j-1];
				}
			}
		}
		
		for(int i=0;i<9;i++){
			for(int j=0;j<9;j++){
				System.out.print(dp[i][j]+"  ");
			}
			System.out.println();
		}
		
		System.out.println(dp[m-1][n-1]);
	}
}

DFS的方法实现:http://www.it610.com/article/5451938.htm


分析:在遇到grid[i][j] = 1的地方,dp[i][j] 设置为0

代码实现:

class DP{
public static void main(String[] args) {
	int m = 3;
	int n = 3;
	int[][] grid = new int[m][n];    //格子中的数组
	grid[1][1] = 1;                  //其中中间值为1
	
	int[][] dp = new int[m][n];
	for(int i=0;i<m;i++){
		for(int j=0;j<n;j++){
			if(i==0||j==0){
				dp[i][j] = 1;
			}
			else if(grid[i][j]==0){
				dp[i][j] = dp[i-1][j] + dp[i][j-1];
			}
		}
	}//打印看一下dp矩阵
	for(int i=0;i<m;i++){
		for(int j=0;j<n;j++){
			System.out.print(dp[i][j]+" ");
		}
		System.out.println();
	}
	System.out.println(dp[m-1][n-1]);
   }
}
1 1 1 
1 0 1 
1 1 2 
2

在[1,1]位置,dp[1][1]设为0.

6.完美平方数问题



猜你喜欢

转载自blog.csdn.net/chenkaibsw/article/details/80160202