动态规划类整理

1.最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

动态规划,状态转移方程:dp[i] = max{A[i], dp[i-1]+A[i]}

	public static int maxSubArray(int[] nums) {
		if (nums == null)
			return 0;
		int len = nums.length;
		int max = nums[0];
		int[] dp = new int[len];
		dp[0] = nums[0];
		for (int i = 1; i < len; i++) {
			if (dp[i - 1] > 0)
				dp[i] = dp[i - 1] + nums[i];
			else
				dp[i] = nums[i];
		}

		for (int i = 0; i < len; i++) {
			if (max < dp[i])
				max = dp[i];
		}
		return max;
	}

不借助dp数组:
一定先判断 if(max<sum)后 if(sum<0);
因为如果全负数的情况,sum=0;会影响导致结果为0;

    public int maxSubArray(int[] nums) {
        if (nums == null)
			return 0;
		int len = nums.length;
		int max = nums[0];
		int sum = 0;
		for(int i=0;i<len;i++) {
			sum=sum+nums[i];
			if(max<sum)
				max=sum;
			 if(sum<0)
				sum=0;
		}
		return max;
    }

2.爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1 阶 + 1 阶
2 阶
思路:跳台阶问题

    public int climbStairs(int n) {
        if (n <= 0)
			return 0;
		if (n < 3)
			return n;
		int f1 = 1, f2 = 2, res = 0;
		for (int i = 3; i <= n; i++) {
			res = f1 + f2;
			f1 = f2;
			f2 = res;
		}
		return res;
    }

300.最长上升子序列
给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
思路:动态规划dp[i] 存储的是以i为最后元素的,最长子序列长度;
因此需要内循环j从0遍历到i,如果存在比nums[i]小的元素,则计算dp转移方程;
dp[i]= max(dp[i], dp[j] + 1);

注意:此类题是O(N2),上升子序列不是连续的;

    public int lengthOfLIS(int[] nums) {
     	if(nums==null)
			return 0;
		int len = nums.length;
		int[] dp=new int[len];
		for(int i=0;i<len;i++){
			dp[i]=1;
		}
		int res = 0;
		for(int i=0;i<len;i++) {
			for(int j=0;j<i;j++) {
				if(nums[i]>nums[j])
					dp[i]=dp[i]>dp[j]+1?dp[i]:dp[j]+1;
			}
			res=res>dp[i]?res:dp[i];
		}
		return res;
    }

674.最长连续递增序列
给定一个未经排序的整数数组,找到最长且连续的的递增序列。

示例 1:

输入: [1,3,5,4,7]
输出: 3
解释: 最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为5和7在原数组里被4隔开。
思路:设计数器count,如果遇到更大的数,则加一;否则,计数器归一;
res始终保存当前最大count值;

	public static int findLengthOfLCIS(int[] nums) {
		if(nums==null)
			return 0;
		int res=1;
		int len = nums.length;
		int cur=nums[0];
		int count=1;
		for(int i=1;i<len;i++) {
			if(nums[i]>cur)
				count++;
			else
				count=1;
			res=res>count?res:count;
			cur=nums[i];
		}
		return res;
	}

最长公共子序列LCS
链接:https://www.nowcoder.com/questionTerminal/c996bbb77dd447d681ec6907ccfb488a
来源:牛客网

对于两个字符串,请设计一个高效算法,求他们的最长公共子序列的长度,这里的最长公共子序列定义为有两个序列U1,U2,U3…Un和V1,V2,V3…Vn,其中Ui&ltUi+1,Vi&ltVi+1。且A[Ui] == B[Vi]。

给定两个字符串A和B,同时给定两个串的长度n和m,请返回最长公共子序列的长度。保证两串长度均小于等于300。

测试样例:
“1A2C3D4B56”,10,“B1D23CA45B6A”,12
返回:6
思路:
动态规划;dp[i][j]表示A字符串的前i字符与B串的前j字符的最长公共子序列的长度;
转移方程:当A[i]==B[j],dp[i][j]=dp[i-1][j-1]+1;
否则dp[i][j]=max{dp[i-1][j],dp[i][j-1]};
注意:子序列问题同子串问题区分;子串是连续的;

	public static int findLCS(String A, int n, String B, int m) {
		int[][] dp = new int[n + 1][m + 1];
		dp[0][0] = 1;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				if (A.charAt(i - 1) == B.charAt(j - 1))
					dp[i][j] = dp[i - 1][j - 1] + 1;
				else
					dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
			}
		}
		return dp[n][m];
	}

最长公共连续子串
输入
abcde
abgde
输出
2
思路:与上题类似,动态规划思想;
主要区别是,dp矩阵意义为A串以i结尾与B串以j结尾的最长公共连续子串;
当A[i-1] = B[j-1]时,dp[i][j] = dp[i -1][j -1] + 1;
A[i-1] != B[j-1]时,以他们结尾的公共子串长度必然为0;
最后从dp中取出长度最大的值即为最长公共子串;

	 public static int findLongest(String A, int n, String B, int m) {
		 int [][]dp=new int[n+1][m+1];
		 int max=0;
		 for(int i=1;i<=n;i++) {
			 for(int j=1;j<=m;j++) {
				 if (A.charAt(i - 1) == B.charAt(j - 1))
						dp[i][j] = dp[i - 1][j - 1] + 1;
				 else
					 dp[i][j]=0;
				 max=Math.max(max, dp[i][j]);
			 }
		 }
		 return max;
	 }

买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

注意你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
思路:遍历数组;
res保存当前最小的值;
max保存当前收益最大的值;
因为如果出现了比当前res小的数,买入更小的res以后序的相同价格卖出,一定比之前的收益高,所有直接替换保证res为当前最小值即可;

    public int maxProfit(int[] prices) {
            	if(prices==null||prices.length==0)
    		return 0;
    	int len = prices.length;
    	int max=0;
    	int res = Integer.MAX_VALUE;
    	for(int i=0;i<len;i++) {
    		res = Math.min(prices[i], res);
    		max = Math.max(max, prices[i]-res);
    	}
    	return max;
    }

买卖股票的最佳时机 II
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
思路:只需遍历数组;如果当前元素比前个元素大,即加入收益;
因为如果后序仍有比当前元素大的元素,再以当前价格买入,后序价格卖出即可,不会丢失任何利润;

    public int maxProfit(int[] prices) {
        if(prices==null||prices.length==0)
    		return 0;
    	int len = prices.length;
    	int res =0;
    	for(int i=1;i<len;i++) {
    		if(prices[i]>prices[i-1])
    			res+=prices[i]-prices[i-1];
    	}
    	return res;
    }

猜你喜欢

转载自blog.csdn.net/sky_noodle/article/details/84103611
今日推荐