題目來源:LeetCode-騰訊-動態規劃
題庫鏈接:LeetCode傳送門
前言
這份題集來源於 LeetCode-騰訊-動態規劃 ,懷着想學習動態規劃的心(帶着害怕面試被問到的恐懼 )做了第一份DP題集,希望可以志同道合的夥伴可以共同進步。
1. 爬樓梯
假設你正在爬樓梯。需要 n 階你才能到達樓頂。
每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?
注意:給定 n 是一個正整數。
示例 1:
輸入: 2
輸出: 2
示例 2:
輸入: 3
輸出: 3
狀態轉移方程:
class Solution {
public int climbStairs(int n) {
if (n == 1) {
return 1;
} else if (n == 2) {
return 2;
}
int a = 1, b = 2;
for (int i = 2; i < n; i++) {
b = a + b;
a = b - a;
}
return b;
}
}
2. 最大子序列和
給定一個整數數組 nums ,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和。
示例:
輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6。
狀態轉移方程:
PS: 表示的是以結尾的子序列的最大值
class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;
int dp = 0, ans = Integer.MIN_VALUE;
for (int i = 0; i < n; i++) {
dp = Math.max(dp + nums[i], nums[i]);
ans = Math.max(dp, ans);
}
return ans;
}
}
3. 買賣股票的最佳時機
給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。
如果你最多隻允許完成一筆交易(即買入和賣出一支股票一次),設計一個算法來計算你所能獲取的最大利潤。
注意:你不能在買入股票前賣出股票。
示例 1:
輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。
示例 2:
輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤爲 0。
狀態轉移方程:
PS:其中 表示 i 天前的最低價格。
class Solution {
public int maxProfit(int[] prices) {
int dp = 0, minPrice = Integer.MAX_VALUE;
//計算狀態轉移方程,通過滾動變量的形式優化空間複雜度
for (int i = 0; i < prices.length; i++) {
minPrice = Math.min(prices[i], minPrice);
dp = Math.max(dp, prices[i] - minPrice);
}
return dp;
}
}
4. 買賣股票的最佳時機 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 。
狀態轉移方程(根據上面的樹形圖推導):
PS:表示第 i 天持有的現金的最大利潤,表示第 i 天持有的股票的最大利潤。
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
if (n < 2) {
return 0;
}
int[][] dp = new int[n][2];
dp[0][0] = 0;
dp[0][1] = -prices[0];
for (int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
}
return dp[n - 1][0];
}
}
可以看一下這篇題解:暴力搜索、貪心算法、動態規劃
(明明貪心就很好做……)
5. 不同路徑
一個機器人位於一個 m x n 網格的左上角 (起始點在下圖中標記爲“Start” )。
機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記爲“Finish”)。
問總共有多少條不同的路徑?
示例 1:
輸入: m = 3, n = 2
輸出: 3
解釋:
從左上角開始,總共有 3 條路徑可以到達右下角。
- 向右 -> 向右 -> 向下
- 向右 -> 向下 -> 向右
- 向下 -> 向右 -> 向右
示例 2:
輸入: m = 7, n = 3
輸出: 28
狀態轉移方程:
class Solution {
public int uniquePaths(int m, int n) {
int[][] dp = new int[n][m];
for (int i = 0; i < n; i++) {
dp[i][0] = 1;
}
for (int i = 0; i < m; i++) {
dp[0][i] = 1;
}
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[n - 1][m - 1];
}
}
(明明排列組合更加方便……)
6. 子集
給定一組不含重複元素的整數數組 nums,返回該數組所有可能的子集(冪集)。
說明:解集不能包含重複的子集。
示例:
輸入: nums = [1,2,3]
輸出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
狀態轉移方程:
PS:其中 表示新找的一個集合, 表示原先找到的集合, 表示新的數字。
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
// 首先添加空集
ans.add(new ArrayList<>());
for (int i = 0; i < nums.length; i++) {
// 由於下面過程會改變容量,所以要先保存
int tSize = ans.size();
// 遍歷已經找到的集合
for (int j = 0; j < tSize; j++) {
List<Integer> temp = new ArrayList<>(ans.get(j));
// 在已經找到的集合的基礎上加上新的數字就是新的集合了
temp.add(nums[i]);
ans.add(temp);
}
}
return ans;
}
}
不知道爲啥這道題在動態規劃題單裏面……(大霧 )
總結
終於寫完了,感覺很多題就用不着去DP,直接貪心就好了,強行DP做到我裂開。由於時間緊迫,題解也沒有寫得很詳細,就將就着看吧,有問題可以留言提出來哦~