一维动态规划小试牛刀
在中,我们讲解了什么是动态规划,以及动态规划解决问题的三步走方案。常言道,光说不练嘴把式,光练不说傻把式,既说又练真把式。那么,本文就用 leetcode 上的几道小题目,小试一下动态规划这把牛刀。
实战
1.最大连续子序列和
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
Input: [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6Follow up:
If you have figured out theO(n)
solution, try coding another solution using the divide and conquer approach, which is more subtle.
分析:题意很简单,就是找出数组中最大的连续子序列和,难点在于题目要求时间复杂度是 O(n)
。暴力枚举也能解决问题,可惜时间复杂度是 O(n^2)
,不满足题目要求。好了,我们用动态规划试试。假设输入为数组 arr
,定义 dp[i]
为以数组元素 arr[i]
结尾的最大连续子序列和,那么我们可以很容易地想到,如果 dp[i-1]<0
,则 dp[i]=arr[i]
,否则, dp[i]=dp[i-1]+arr[i]
。代码如下:
public int maxSubArr(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int[] dp = new int[arr.length];
dp[0] = arr[0];
int max = dp[0];
for (int i = 1; i < arr.length; i++) {
dp[i] = dp[i - 1] < 0 ? arr[i] : dp[i - 1] + arr[i];
max = Math.max(max, dp[i]);
}
return max;
}
2.最大连续递增子序列
Given an unsorted array of integers, find the length of longest continuous increasing subsequence.
Example 1:
Input: [1,3,5,4,7]
Output: 3
Explanation: The longest continuous increasing subsequence is [1,3,5], its length is 3. Even though [1,3,5,7] is also an increasing subsequence, it's not a continuous one where 5 and 7 are separated by 4.Example 2:
Input: [2,2,2,2,2]
Output: 1
Explanation: The longest continuous increasing subsequence is [2], its length is 1.Note: Length of the array will not exceed 10,000.
分析:有了第一题的经验,这道题就容易多了。假设输入为数组 arr
,定义 dp[i]
为 arr[i]
结尾的最大连续递增子序列的长度,则如果 arr[i]>arr[i -1]
, 则 dp[i]=dp[i -1]+1
, 否则 dp[i]=1
。代码如下:
public int findLenOfLCIS(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int[] dp = new int[arr.length];
dp[0] = 1;
int max = dp[0];
for (int i = 1; i < arr.length; i++) {
dp[i] = arr[i] > arr[i - 1] ? dp[i - 1] + 1 : 1;
max = Math.max(max, dp[i]);
}
return max;
}
3.零钱兑换
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
Example 1:
coins = [1, 2, 5], amount = 11
return 3 (11 = 5 + 5 + 1)Example 2:
coins = [2], amount = 3
return -1Note: You may assume that you have an infinite number of each kind of coin.
分析:定义数组 coins
为不同面值的硬币,定义 dp[i]
为凑 i
元需要最少的硬币数,则 dp[i]=1+min{dp[i -coins[1]],...,dp[i -coins[k]]}
。代码如下:
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, -1);
dp[0] = 0;
for (int i = 1; i < dp.length; i++) {
for (int coin : coins) {
if (i >= coin && dp[i - coin] != -1) {
dp[i] = dp[i] == -1 ? 1 + dp[i - coin] : Math.min(dp[i], 1 + dp[i - coin]);
}
}
}
return dp[amount];
}
4.打家劫舍
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
Example 1:
Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3), Total amount you can rob = 1 + 3 = 4.Example 2:
Input: [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1), Total amount you can rob = 2 + 9 + 1 = 12.
分析:假设输入数组 arr
,定义 dp[i]
为子数组 arr[0...i]
能够抢到的钱的最大值,则 dp[i]=max{dp[i -1],dp[i -2]+arr[i]}
。代码如下:
public int rob(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
} else if (arr.length == 1) {
return arr[0];
}
int[] dp = new int[arr.length];
dp[0] = arr[0];
dp[1] = Math.max(arr[0], arr[1]);
for (int i = 2; i < arr.length; i++) {
dp[i] = Math.max(dp[i - 1], dp[i - 2] + arr[i]);
}
return dp[arr.length - 1];
}
小结
本文通过几道leetcode上的题目,演示了如何利用动态规划解决问题。看似比较棘手的问题,只要能够定义出 dp
数组的含义,并且推导出状态转移方程,就可以轻松解决之。目前,我们欠缺的只是这种经验,只要努力练习,培养出这种解决问题的直觉,起码一维动态规划问题不在话下。接下来,我们会再介绍二维动态规划问题,请大家拭目以待。