1. 程式人生 > >【LeetCode】動態規劃(上篇共75題)

【LeetCode】動態規劃(上篇共75題)

【5】 Longest Palindromic Substring

給一個字串,需要返回最長迴文子串

解法:dp[i][j] 表示 s[i..j] 是否是迴文串,轉移方程是 dp[i][j] = 1 (if dp[i+1][j-1] = 1 && s[i] == s[j]),初始化條件是 if (s[i] == s[j] && (i == j || i == j -1))

時間複雜度和空間複雜度都是O(n^2)

 1 class
Solution { 2 public: 3 string longestPalindrome(string s) { 4 const int n = s.size(); 5 vector<vector<int>> dp(n, vector<int>(n, 0)); 6 for (int i = n - 1; i >= 0; --i) { 7 for (int j = i; j < n; ++j) { 8 if ((i == j || i + 1
== j) && s[i] == s[j]) { 9 dp[i][j] = 1; 10 } else { 11 if (dp[i+1][j-1] && s[i] == s[j]) { 12 dp[i][j] = 1; 13 } 14 } 15 } 16 } 17 int maxLen = 0
, begin = 0, end = 0; 18 for (int i = 0; i < n; ++i) { 19 for (int j = i; j < n; ++j) { 20 if (dp[i][j] && j - i + 1 > maxLen) { 21 begin = i, end = j, maxLen = j - i + 1; 22 } 23 } 24 } 25 return s.substr(begin, maxLen); 26 27 } 28 };
View Code

  

【10】 Regular Expression Matching

正則字串匹配,同44,不會做

 

 

【32】 Longest Valid Parentheses

 

 

【44】 Wildcard Matching

 

【53】 Maximum Subarray

 

【62】 Unique Paths

給了一個 m * n 的區域,只能往右或者往下走,問從左上角走到右下角有幾種走法。

解法: 我們用 f[i][j]  表示走到座標 (i,j)有幾種走法,轉移方程為: f[i][j] = f[i-1][j] + f[i][j-1] 

初始化條件為: f[0][j] = f[i][0] = 1, f[0][0] = 0;

時間複雜度,空間複雜度都為 O(n^2),可以滾動陣列優化,但是我不想寫了--

 1 class Solution {
 2 public:
 3     int uniquePaths(int m, int n) {
 4         vector<vector<int>> path(m, vector<int>(n, 1));
 5         for(int i = 1;  i < m; ++i){
 6             for(int j = 1; j < n; ++j){
 7                 path[i][j] = path[i-1][j] + path[i][j-1];
 8             }
 9         }
10         return path[m-1][n-1];
11         
12     }
13 };
View Code

 

【63】 Unique Paths II

給了一個 m * n 的區域,但是區域中有些座標上擺了障礙物,還是隻能往右走或者往下走,問從左上角到右下角有幾種走法。

解法:用 f[i][j] 表示走到座標 (i, j)有幾種走法,轉移方程為: 如果座標(i,j)上有障礙物,f[i][j] = 0, 沒有障礙物的話,  f[i][j] = f[i-1][j] + f[i][j-1] 

初始化條件為: f[0][j] = f[i][0] = 1, f[0][0] = 0;

時間複雜度,空間複雜度都是O(n^2),可以用滾動陣列優化,我也懶得寫了。

 1 class Solution {
 2 public:
 3     int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
 4         
 5         const int m = obstacleGrid.size();
 6         const int n = obstacleGrid[0].size();
 7         
 8         vector<vector<int>> ans(m, vector<int>(n, 1));
 9         
10         for(int i = 0; i < m; ++i){
11             if(obstacleGrid[i][0] == 1){
12                 for(int k = i; k < m; ++k){
13                     ans[k][0] = 0;
14                 }
15                 break;
16             }
17         }
18         
19         for(int j = 0; j < n; ++j){
20             if(obstacleGrid[0][j] == 1){
21                 for(int k = j; k < n; ++k){
22                     ans[0][k] = 0;
23                 }
24                 break;
25             }
26         }
27         
28         for(int i = 1; i < m; ++i){
29             for(int j = 1; j < n; ++j){
30                 if(obstacleGrid[i][j] == 1){
31                     ans[i][j] = 0;
32                 }
33                 else{
34                     ans[i][j] = ans[i-1][j] + ans[i][j-1];
35                 }
36             }
37         }
38         
39         return ans[m-1][n-1];
40     }
41 };
View Code

 

【64】 Minimum Path Sum

 給了一個 m * n 的區域,每個座標(i, j)上有一個非負整數,只能往右走或者往下走,問從左上角到右下角經過路徑的最小和是多少。

 解法:f[i][j] 表示在座標(i, j)的時候經過的最小路徑和,轉移方程為: f[i][j] = min(f[i-1][j], f[i][j-1]) + grid[i][j]

 初始化條件為:f[0][0] = grid[0][0], f[0][j] = f[0][j-1] + grid[0][j], f[i][0] = f[i-1][0] + grid[i][0]

時間複雜度,空間複雜度都是 O(n^2)

 1 class Solution {
 2 public:
 3     int minPathSum(vector<vector<int>>& grid) {
 4         const int m = grid.size();
 5         const int n = grid[0].size();
 6         
 7         for(int i = 1; i < m; ++i){
 8             grid[i][0] += grid[i-1][0]; 
 9         }
10         for(int j = 1; j < n; ++j){
11             grid[0][j] += grid[0][j-1];
12         }
13         
14         for(int i = 1; i < m; ++i){
15             for(int j = 1;j < n; ++j){
16                 grid[i][j] = min(grid[i-1][j], grid[i][j-1]) + grid[i][j];
17             }
18         }
19         return grid[m-1][n-1];
20     }
21 };
View Code

 

  【70】 Climbing Stairs

 有n層樓梯要爬,一次要麼爬一層,要麼爬兩層,問爬n層有多少種爬法。

 解法: f[i]  表示爬 i 層樓梯有多少種爬法, 轉移方程為:f[i] = f[i-1] + f[i-2]

初始化條件為: f[1] = 1,  f[2] = 2

可以用滾動陣列優化,時間複雜度空間複雜度優化前都是O(n) 

 1 class Solution {
 2 public:
 3     int climbStairs(int n) {
 4         int pre = 1, cur = 2;
 5         if(n == 1) return 1;
 6         int i = 2;
 7         while( n > i ){
 8             int temp = pre + cur;
 9             pre = cur;
10             cur = temp;
11             i++;
12         }
13         
14         return cur;
15     }
16 };
View Code

 

【72】 Edit Distance

 

85 Maximal Rectangle

 

 

 

87 Scramble String

 

 

91 Decode Ways

 

 

 

95 Unique Binary Search Trees II

 

 

 

96 Unique Binary Search Trees

97 Interleaving String

 

 

 

 

【115】 Distinct Subsequences

 

 

 

【120】 Triangle

數字三角形,給了一個數字三角形(n行,每行最多n個數),問最底層到塔尖能經過數字的最小和是多少。

解法:dp[i][j] = triangle[i][j] + min(dp[i+1][j], dp[i+1][j+1]); dp陣列可以優化成一維

初始化條件是:dp[n-1][] = triangle[n-1][];

 1 class Solution {
 2 public:
 3     int minimumTotal(vector<vector<int>>& triangle) {
 4         int n = triangle.size();
 5         vector<int> f{triangle[n-1].begin(), triangle[n-1].end()};
 6         for (int i = n - 2; i >=0 ; --i) {
 7             for (int k = 0; k <= i; ++k) {
 8                 f[k] = min(f[k], f[k+1]) + triangle[i][k];
 9             }
10         }
11         return f[0];
12     }
13 };
View Code

  

【121】 Best Time to Buy and Sell Stock

股票三連擊,給了一個數組表示第i天股票的價格, 問在最多交易一次的情況下(買賣各一次),最大利益是多少。

解法:用個變數minCost標記當前走過的最小值,如果 prices[i] - minCost > ans,  更新ans  

 1 class Solution {
 2 public:
 3     int maxProfit(vector<int>& prices) {
 4         int ans = 0;
 5         int minCost = INT_MAX;
 6         for (int i = 0; i < prices.size(); ++i) {
 7             if (i > 0) {
 8                 ans = max(ans, prices[i] - minCost);
 9                 //minCost = min(minCost, prices[i]);
10             }
11             minCost = min(minCost, prices[i]);
12         }
13         return ans;
14     }
15 };
View Code

  

【123】 Best Time to Buy and Sell Stock III

股票三連擊,給了一個數組表示第i天股票的價格,問在最多交易兩次的情況下,最大利益是多少。(股票三連擊都有個前提條件是,在買入之前,必須把前一支賣出)

哇,我還是不會寫,就和188題差不多。。。。有點複雜。。。。

 

【132】 Palindrome Partitioning II

給了一個字串s,要求他的最小割數(cut),使得割出來的每個單詞都是迴文。

解法:f[i] 表示前i個字元的最小割數。

轉移方程為:f[i] = min(f[start] + 1), if (s[start..i-1]是迴文串,這個也可以用dp求,見第五題) (0  <= start < i)

初始化條件:f[0] = 0, f[1] = 0

//程式碼有點出入,有空用我的方法重寫下。 

 1 class Solution {
 2 public:
 3     int minCut(string s) {
 4         int n = s.size();
 5         if (n == 0) { return 0; }
 6         
 7         //p[i][j] 表示 s[i..j] 是否是個迴文串
 8         vector<vector<int>> p(n, vector<int>(n, 0));
 9         for (int i = n -1; i >= 0; --i) {
10             for (int j = i; j < n; ++j) {
11                 if (s[i] == s[j] && (j - i < 2 || p[i+1][j-1])) {
12                     p[i][j] = 1;
13                 }
14             }
15         }
16         
17         for (int i = 0; i < n; ++i) {
18             for (int j = 0; j < n; ++j) {
19                 //printf("%d, ", p[i][j]);
20             }
21             //printf("\n");
22         }
23         
24         // cut[i] 表示從s[i..n)最少切割幾刀
25         vector<int> cut(n+1, INT_MAX);
26         cut[n] = -1;
27         for (int i = n - 1; i >= 0; --i) {
28             for (int j = i; j < n; ++j) {
29                 if (p[i][j]) {
30                     cut[i] = min(cut[j+1] + 1, cut[i]);
31                 }
32             }
33         }
34         return cut[0];
35         
36     }
37 };
View Code  
 1 class Solution {
 2 public:
 3     int minCut(string s) {
 4         const int n = s.size();
 5         //cal p array
 6         vector<vector<int>> p(n, vector<int>(n, 0));
 7         for (int i = n-1; i >= 0; --i) {
 8             for(int j = i; j < n; ++j) {
 9                 if (s[i] == s[j] && (j - i < 2 || p[i+1][j-1])) {
10                     p[i][j] = 1;
11                 }
12             }
13         }
14         
15         for (int i = 0; i < n; ++i) {
16             for (int j = 0; j < n; ++j) {
17                 //printf("%d ", p[i][j]);
18             }
19             //cout << endl;
20         }
21         
22         //dp
23         vector<int> f(n + 1, 0);
24         f[0] = -1; f[1] = 0;
25         for (int i = 1; i <= n; ++i) {
26             f[i] = i - 1;
27             for (int start = 0; start <= i - 1; ++start) {
28                 if (p[start][i-1]) {
29                     f[i] = min(f[i], f[start] + 1);
30                 }
31             }
32         }
33         for (int i = 0; i <= n; ++i) {
34             //printf("%d ", f[i]);
35         }
36         //cout << endl;
37         
38         return f[n];
39         
40     }
41 };
View Code

 

【139】 Word Break

 給了一個非空的字串和一個單詞字典,問這個字串能否用字典裡面的單詞表示。

解法:f[i] 表示字串的前i個字母能否用字典表示。

轉移方程為:if (f[start] == 1 && s[start..i-1]是一個字典中的單詞) { f[i] = 1; }

初始化條件為:f[0] = 1, f[1..n] = 0;

時間複雜度是 O(n^2), 空間複雜度是 O(n) 

 1 class Solution {
 2 public:
 3     bool wordBreak(string s, vector<string>& wordDict) {
 4         const int n = s.size();
 5         if (n == 0) {
 6             return true;
 7         }
 8         vector<bool> f(n+1, false);
 9         f[0] = true;
10         for (int i = 1; i <= s.size(); ++i) {
11             for (int start = 0; start <= i; ++start) {
12                 if (f[start] && i - start >= 1) {
13                     //s[start..i-1]
14                     string strsub = s.substr(start, i - start);
15                     if (find(wordDict.begin(), wordDict.end(), strsub) != wordDict.end()) {
16                         f[i] = true;
17                         break;
18                     }
19                 }
20             }
21         }
22         return f[n];
23     }
24 };
View Code

 

【140】 Word Break II

 跟上一題差不多,給了一個非空的字串和一個單詞字典,返回這個字串能用字典表示的所有方案。如果沒有方案,就返回一個空陣列。

解法: 我的解法MLE了,想法就是 f[i] 表示前i個字元能否被字典表示,arr[i][....]表示前i個字元的所有方案數。

轉移方程是:if (f[start] == 1 && s[start..i-1]是一個字典中的單詞) { f[i] = 1;  arr[i] = arr[start] + s[start .. i-1] }

後來,看了soulmachine的答案,他是用 dp[i][j] 表示 s[i..j-1]  是否是字典中的元素(代替我的arr陣列),最後dfs出答案,能過。 

 1 class Solution {
 2 public:
 3     
 4     void genPath(string s, vector<vector<bool>>& arr, vector<string>& ans, int cur, vector<string>& temp) {
 5         if (cur == s.size()) {
 6             string str;
 7             for (auto ele : temp) {
 8                 if (str.empty()) {
 9                     str += ele;
10                 } else {
11                     str = str + " " + ele;                   
12                 }
13             }
14             ans.push_back(str);
15         }
16         for (int i = cur + 1; i <= s.size(); ++i) {
17             if (arr[cur][i]) {
18                 string strsub = s.substr(cur, i - cur);
19                 temp.push_back(strsub);
20                 genPath(s, arr, ans, i, temp);
21                 temp.pop_back();
22             }
23         }
24     }
25     
26     
27     vector<string> wordBreak(string s, vector<string>& wordDict) {
28         const int n = s.size();
29         if (n == 0) {
30             return vector<string>();    
31         }
32         vector<vector<bool>> arr(n+1, vector<bool>(n+1, false));
33         vector<bool> f(n+1, false);
34         f[0] = true;
35         
36         for (int i = 1; i <= n; ++i) {
37             for (int start = 0; start <= i; ++start) {
38                 // s[start..i-1]
39                 if (f[start] && i - start >= 1) {
40                     string strsub = s.substr(start, i - start);
41                     if (find(wordDict.begin(), wordDict.end(), strsub) != wordDict.end()) {
42                         f[i] = true;
43                         arr[start][i] = true; //表示s[start,i)是一個合法單詞
44                         /*
45                         for (auto ele : arr[start]) {
46                             string ans = ele + " " + strsub;
47                             arr[i].push_back(ans);
48                         }
49                         if (arr[start].empty()) {
50                             arr[i].push_back(strsub);
51                         }
52                         */
53                     }
54                 }
55             }
56         }
57         
58         vector<string> ans, temp;
59         if (f[n] == false) {
60             return ans;
61         }
62         genPath(s, arr, ans, 0, temp);
63         
64         return ans;
65     }
66 };
View Code

 

【152】 Maximum Product Subarray

題目就是給了一個整數陣列nums,裡面元素可正,可負,可零,返回子陣列累乘的最大乘積。

題解:思路是所有的子陣列都會以某一個位置結束,所以,如果求出以每一個位置結尾的子陣列的最大的累乘積,那麼在這麼多最大累乘積中最大的那個就是最終的結果.問題是如何求出所有以 下標 i 為結尾的子陣列的最大累乘積。 假設以 nums[i-1] 為結尾的最大累乘積是 preMax, 最小累乘積是 preMin, 那麼以nums[i] 為結尾的最大累乘積就是 max(preMax * nums[i], preMin * nums[i], nums[i]) (為啥會有nums[i]本身,比如{0, -1, 200000} 這個陣列)

時間複雜度是O(N),空間複雜度是O(1).

 1 class Solution {
 2 public:
 3     //時間複雜度O(N),空間複雜度O(1)
 4     //思路是所有的子陣列都會以某一個位置結束,所以,如果求出以每一個位置結尾的子陣列的最大的累乘積,那麼在這麼多最大累乘積中最大的那個就是最終的結果.
 5     //問題是如何求出所有以 下標 i 為結尾的子陣列的最大累乘積。 假設以 nums[i-1] 為結尾的最大累乘積是 preMax, 最小累乘積是 preMin, 那麼以nums[i] 為結尾的最大累乘積就是 max(preMax * nums[i], preMin * nums[i], nums[i]) (為啥會有nums[i]本身,比如{0, -1, 200000} 這個陣列)
 6     int maxProduct(vector<int>& nums) {
 7         int ans = INT_MIN;
 8         const int n = nums.size();
 9         if (n == 0) {return 0;}
10         int preMax = 1, preMin = 1;
11         for (int i = 0; i < n; ++i) {
12             int fromMax = nums[i] * preMax, fromMin = nums[i] * preMin;
13             preMax = max(max(fromMax, fromMin), nums[i]), preMin = min(min(fromMax, fromMin), nums[i]);
14             ans = max(ans, preMax);
15         }
16         return ans;
17     }
18 };
View Code

 

【174】 Dungeon Game

題目意思大概就是類似於勇士救公主那個遊戲,勇士一開始在矩陣的左上角,公主一開始在矩陣的右下角,矩陣的每個格子裡面要麼有妖怪(勇士會掉血),要麼有靈藥(勇士會加血),為了能最快的救公主,勇士只能往右或者往下走。如果勇士在途中的任何一個房間血量小於等於0,那他馬上就掛了。問他能救到公主的情況下,一開始的最小血量是多少。矩陣大小為 n*m

題解:我們假設他在公主的房間的血量正好為0,那麼所求的值就是他一開始能經過這一路的最小值。dp[i][j] 表示他在座標(i, j)能走完剩下旅程的最小血量。

轉移方程為:dp[i][j] = min(bottom, right); bottom = abs(min(dungeon[i][j] - dp[i+1][j], 0)); right = abs(min(dungeon[i][j] - dp[i][j+1], 0))

初始化條件為:最右邊:dp[i][m-1] = abs (min(dungeon[i][m-1], 0)); 最下邊:dp[n-1][j] = abs(min(dungeon[n-1][j], 0))

時間複雜度和空間複雜度都是O(n^2)

 1 class Solution {
 2 public:
 3     int calculateMinimumHP(vector<vector<int>>& dungeon) {
 4         int n = dungeon.size(), m = dungeon[0].size();
 5         vector<vector<int>> dp(n, vector<int>(m, 0));
 6         
 7         //init
 8         dp[n-1][m-1] = dungeon[n-1][m-1] > 0 ? 0 : abs(dungeon[n-1][m-1]);
 9         for (int i = m - 2; i >= 0 ; --i) {
10             dp[n-1][i] = (dungeon[n-1][i] - dp[n-1][i+1]) >= 0 ? 0 : abs(dungeon[n-1][i] - dp[n-1][i+1]);
11         }
12         for (int i = n - 2; i >= 0; --i) {
13             dp[i][m-1] = (dungeon[i][m-1] - dp[i+1][m-1]) >= 0 ? 0 : abs(dungeon[i][m-1] - dp[i+1][m-1]);
14         }
15         
16         //dp
17         for (int i = n - 2; i >= 0; --i) {
18             for (int j = m - 2; j >= 0; --j) {
19                 int bottom = (dungeon[i][j] - dp[i+1][j]) >= 0 ? 0 : abs(dungeon[i][j] - dp[i+1][j]);
20                 int right = (dungeon[i][j] - dp[i][j+1]) >= 0 ? 0 : abs(dungeon[i][j] - dp[i][j+1]); 
21                 dp[i][j] = min(bottom, right);
22             }
23         }
24         return dp[0][0]+1;
25     }
26     
27 };
View Code

 

【188】 Best Time to Buy and Sell Stock IV

哇,不會做的題orz。。。

 

【198】 House Robber

有個小偷有天晚上想偷一排房子,但是如果他偷了相鄰的兩個房子,就會自動報警。給了一個數組,表示每個房子裡面有多少錢,問這個小偷在安全的情況下最多能偷多少錢。

解法:dp[i] 表示他偷了第i個房子的最大錢數。轉移方程為:dp[i] = max(dp[i-2], dp[i-3]) + nums[i]。初始化條件,dp[0] = nums[0], dp[1] = max(nums[0], nums[1])。

我程式碼裡面是從後往前偷,但是意思是一樣的。

 1 class Solution {
 2 public:
 3     int rob(vector<int>& nums) {
 4         //f[i] 
 5         //f[i] = max(f[i-2], f[i-3])
 6         int n = nums.size();
 7         if (n == 0) {
 8             return 0;
 9         }
10         if (n == 1) {
11             return nums[0];
12         }
13         vector<int> f(n, 0);
14         f[n-1] = nums[n-1];
15         f[n-2] = max(f[n-1], nums[n-2]);
16         for (int i = n - 3; i >= 0; --i) {
17             if (i == n - 3) {
18                 f[i] = f[n-1] + nums[i];
19             } else {
20                 f[i] = max(f[i+2], f[i+3]) + nums[i];
21             }
22     
23         }
24         return max(f[0], f[1]);
25     }
26 };
View Code

 

 【213】 House Robber II

跟198差不多,就是一個小偷有天晚上想偷一排房子,只不過這排房子排成了一個環形,如果他偷了兩個相鄰的房子,就會自動報警。給了一個數組,表示每個房子裡面有多少錢,問這個小偷在安全的情況下最多今晚能偷多少錢。

解法:這個題目和198不同的地方在於,如果這個小偷偷了第一個房子,那麼他就不能偷最後一個房子,同理,偷了最後一個也不能偷第一個。所以,就是把陣列拆成兩部分,nums1 = nums[0, n-1],nums2 = nums[1, n],然後分別用198的演算法,再比較出一個最大值。

 1 class Solution {
 2 public:
 3     int robb (vector<int> & nums) {
 4         const int n = nums.size();
 5         if (n == 0) {
 6             return 0;
 7         } 
 8         if (n == 1) {
 9             return nums[0];
10         }
11         vector<int> f(n, 0);
12         f[0] = nums[0];
13         f[1] = nums[1];
14         f[2] = f[0] + nums[2];
15         for (int i = 3; i < n; ++i) {
16             f[i] = max(f[i-2], f[i-3]) + nums[i];
17         }
18         return max(f[n-1], f[n-2]);
19     }
20     int rob(vector<int>& nums) {
21         const int n = nums.size();
22         if (n == 0) {
23             return 0;
24         }
25         if (n == 1) {
26             return nums[0];
27         }
28         if (n == 2) {
29             return max(nums[0], nums[1]);
30         }
31         vector<int> nums0(nums.begin(), nums.end()-1);
32         vector<int> nums1(nums.begin()+1, nums.end());
33         return max(robb(nums0), robb(nums1));
34     }
35 };
View Code

  

【221】 Maximal Square

給個二維0/1矩陣,問全1的正方形最大面積是多少. 

解法:dp[i][j] 表示以座標 (i, j)  為右下角的最大的正方形的邊長。轉移方程為:dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1 (if (matrix[i][j] == 1))。初始化條件, if (matrix[i][j]) {dp[0][j] = 1, dp[i][0] = 1}。

時間複雜度和空間複雜度都是 O(n^2),應該可以用滾動陣列優化空間複雜度。

 1 class Solution {
 2 public:
 3     int maximalSquare(vector<vector<char>>& matrix) {
 4         if (matrix.size() == 0) return 0;
 5         const int n = matrix.size(), m = matrix[0].size();
 6         vector<vector<int>> dp(n, vector<int>(m));
 7         int maxLength = 0;
 8         for (int i = 0; i < n; ++i) {
 9             for (int j = 0; j < m; ++j) {
10                 if (i == 0 || j == 0) {
11                     dp[i][j] = matrix[i][j] - '0';
12                 } else {
13                     if (matrix[i][j] == '1') {
14                         dp[i][j] = min(min(dp[i-1][j-1], dp[i-1][j]), dp[i][j-1]) + 1;
15                     }
16                     
17                 //    maxLength = max(dp[i][j], maxLength);
18                 }
19                 maxLength = max(dp[i][j], maxLength);
20             }
21         }
22         return maxLength * maxLength;
23             
24     }
25 };
View Code

 

【256】 Paint House

現在有n個房子,3種顏色,每間房子塗每一種顏色都有不同的花費代價,給了一個 [n*3] 的花費矩陣costs[n * 3],  要求相鄰的兩個房子塗的顏色不能一樣,問塗完所有房子的最小花費。

解法:dp[i][j] 表示在第i間房子塗第j種顏色的情況下的最小花費。轉移方程為:dp[i][j] = min(dp[i-1][k]) + costs[i][j] (k != j), 初始化條件為:dp[0] = costs[0]

 這種解法可以優化成二維滾動陣列,空間複雜度上優化到O(1),時間複雜度是O(n) 

 1 class Solution {
 2 public:
 3     int minCost(vector<vector<int>>& costs) {
 4         const int n = costs.size();
 5         if (n == 0) {
 6             return 0;
 7         }
 8         vector<vector<int>> dp(2, vector<int>(3, INT_MAX));
 9         for (int i = 0; i < 3; ++i) {
10             dp[0][i] = costs[0][i];
11         }
12         for (int i = 1; i < n; ++i) {
13             for (int j = 0; j < 3; ++j) {
14                 dp[1][j] = INT_MAX;
15                 for (int k = 0; k < 3; ++k) {
16                     if (k != j) {
17                         dp[1][j] = min(dp[0][k] + costs[i][j], dp[1][j]);
18                     }                  
19                 }
20             }
21             swap(dp[0], dp[1]);
22         }  
23         return min(dp[0][0], min(dp[0][1], dp[0][2]));
24     }
25 };
View Code

 

【264】 Ugly Number II

醜數的定義是隻能被2,3,5整除的數,前10個醜數是[1, 2, 3, 4, 5, 6, 8, 9, 10, 12],輸入n,返回第n個醜數,比如,輸入10,返回12。 

解法:我用了一個小根堆,和一個set,一開始堆裡面只有一個元素,進行 n-1次堆操作,每次都是把最小的元素 ele 彈出,然後把 ele*2, ele*3, ele*5 這三個數中沒有進過堆的數push進去,用set標記哪些數字進過堆。這種方法會爆int,所以用long long。

discuss還有另外一種dp解法,就是dp[i]表示第i個醜數,此外用三個指標分別指向需要 *2, *3, *5 的元素,每次都把這三個數中乘完最小的放進陣列,然後指標加一。注意重複元素時,另外的一個指標也需要後移,比如6。

我的解法時間複雜度是O(n).

 1 class Solution {
 2 public:
 3     int nthUglyNumber(int n) {
 4         priority_queue<long long, vector<long long>, greater<long long>> que;
 5         que.push(1);
 6         long long ans = 1;
 7         set<long long> st;
 8         st.insert(1);
 9         for (int i = 1; i <= n; ++i) {
10             ans = que.top();
11             que.pop();
12             if (st.find(ans * 2) == st.end()) {
13                 que.push(ans * 2); 
14                 st.insert(ans * 2);
15             }
16             if (st.find(ans * 3) == st.end()) {
17                 que.push(ans * 3);  
18                 st.insert(ans * 3);
19             }
20             if (st.find(ans * 5) == st.end()) {
21                 que.push(ans * 5);  
22                 st.insert(ans * 5);
23             }
24         }
25         return (int)ans;
26     }
27 };
View Code

 

【265】 Paint House II

現在有n個房子,k種顏色,每間房子塗一種顏色都有不同的花費代價,給了一個[n*k]大小的花費矩陣costs[n,k],要求相鄰的房子顏色不能一樣, 問塗完所有的房子最小花費。

解法:dp[i][j] 表示在第i間房子塗第j種顏色的情況下的最小花費。轉移方程為:dp[i][j] = min(dp[i-1][k]) + costs[i][j] (k != j), 初始化條件為:dp[0] = costs[0]

這種解法可以優化成二維滾動陣列,空間複雜度上優化到O(k),時間複雜度是O(n*k^2) 

相關推薦

LeetCode動態規劃75

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } 【5】 Longest Palindromic Substring 給一個字串,需要返回最長迴文子串 解法:dp[i][j] 表示 s[i..j] 是否是迴文串,轉移方程是

leetcode動態規劃之01揹包問題

先學會手動填動態規劃的表  後面的顏色塊值是根據前面的顏色塊值計算出來的,不懂就留言 #include<iostream> #include<vector> using namespace std; int Knapsack(vector<i

leetcode動態規劃

決定減少程式碼數量,增加程式碼質量,寫得多了並不理解,也只是徒抄而已。 動態規劃:每次決策依賴於當前狀態,又隨即引起狀態的轉移。一個決策序列就是在變化的狀態中產生出來的,所以,這種多階段最優化決策解決問題的過程就稱為動態規劃。 5. 最長迴文子串 dp[j][i

BZOJ4311: 向量線段樹分治板子

char 給定 dot 區間 int() str friend 所有 bre 題解 我們可以根據點積的定義,垂直於原點到給定點構成的直線作一條直線,從正無窮往下平移,第一個碰到的點就是答案 像什麽,上凸殼哇 可是……動態維護上凸殼? 我們可以離線,計算每個點能造成貢獻的一個

ASP.NET Core處理異常

關心 指向 然而 sub 相關 pri roo epon netcore 依照老周的良好作風,開始之前先說點題外話。 前面的博文中,老周介紹過自定義 MVC 視圖的搜索路徑,即向 ViewLocationFormats 列表添加相應的內容,其實,對 Razor Page

LeetCode題解動態規劃:從新手到專家(一

【LeetCode題解】動態規劃:從新手到專家(一) 文章標題借用了Hawstein的譯文《動態規劃:從新手到專家》。 1. 概述 動態規劃( Dynamic Programming, DP)是最優化問題的一種解決方法,本質上狀態空間的狀態轉移。所謂狀態轉移是指每個階段的最優狀態(對應於

C++—多型動態多型

一、多型 1、 概念:同一事物表現出的多種形態,同一操作作用於不同的物件,可以有不同的解釋,產生不同的執行結果。在執行時,可以通過指向基類的指標,來呼叫實現派生類中的方法。 2、 舉例子: #include<windows.h> class WashRoom { pu

演算法之動態規劃動態規劃DP詳解

一、基本概念 動態規劃(dynamic programming)是運籌學的一個分支,是求解決策過程(decision process)最優化的數學方法。20世紀50年代初美國數學家R.E.Bellman等人在研究多階段決策過程(multistep d

1leetcode-72 動態規劃 編輯距離

ade 均可 distance 刪除 new ret min sta 插入 (沒思路,很典型,重要) 給定兩個單詞 word1 和 word2,計算出將 word1 轉換成 word2 所使用的最少操作數 。 你可以對一個單詞進行如下三種操作: 插入一個字符 刪

leetcodeWord Breakpython

條件 text for -m 是我 tex eas sso false 思路是這種。我們從第一個字符開始向後依次找,直到找到一個斷句的地方,使得當前獲得的子串在dict中,若找到最後都沒找到。那麽就是False了。 在找到第一個後,接下來找下一個斷句處,當然是從第

經典動態規劃

答案 意思 行修改 優化 待修改 最長 長度 u+ mem 五道經典動態規劃問題1)最大子序列和題目描述:一個序列,選和最大的子序列轉移方程:sum[i]=max{sum[i-1]+a[i],a[i]}當前元素的狀態是:自己單獨一組還是並到前面最後的答案max{sum[i]

Foreign動態規劃 [分治][DP]

tex oid ans script 決策 cst pen 因此 out 動態規劃   Time Limit: 50 Sec Memory Limit: 128 MB Description   一開始有n個數,一段區間的價值為這段區間相同的數的對數。  我們想

LeetCode棧 stack40

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } 【20】Valid Parentheses  【42】Trapping Rain Water  【71】Simplify Path  【84】

LeetCode佇列 queue8

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } 【346】Moving Average from Data Stream  【353】Design Snake Game  【363】Max Sum of

LeetCode貪心 greedy38

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } 【44】Wildcard Matching  【45】Jump Game II  【55】Jump Game  【122】Best Time

LeetCode堆 heap31

【23】 Merge k Sorted Lists 【215】 Kth Largest Element in an Array (無序陣列中最小/大的K個數) 給了一個無序陣列,可能有重複數字,找到第 k 個最大的元素並且返回這個元素值。 題解:直接用直接用個堆儲存陣列中最大的 K 個數。時間複雜度是

九章演算法高階班筆記5.動態規劃

大綱: cs3k.com 滾動陣列 House Robber I/II Maximal Square 記憶化搜尋 Longest Increasing Subsequence Coin in a line I/II/III   什麼是動態

LeetCode排序 sort20

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } 【56】Merge Intervals  【57】Insert Interval  【75】Sort Colors  【147】Inserti

LeetCode字串 string112

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } 【3】Longest Substring Without Repeating Characters  【5】Longest Palindromic Substring

LeetCode抽樣 sampling4

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px } span.