515. Paint House
這裏有n
個房子在一列直線上,現在我們需要給房屋染色,分別有紅色藍色和綠色。每個房屋染不同的顏色費用也不同,你需要設計一種染色方案使得相鄰的房屋顏色不同,並且費用最小,返回最小的費用。
費用通過一個n
x3
的矩陣給出,比如cost[0][0]
表示房屋0
染紅色的費用,cost[1][2]
表示房屋1
染綠色的費用。
提示:f[i][j]爲第i棟房子塗成j顏色所需的最小花費。最後返回的是第n-1棟房子(從0開始),三個裏面的最小值。
由於計算每一個房子時只需要前一次的數據,所以用滾動數組來減少空間。
答案:
class Solution {
public:
/**
* @param costs: n x 3 cost matrix
* @return: An integer, the minimum cost to paint all houses
*/
int minCost(vector<vector<int>> &costs) {
int n = costs.size();
if (!n) return 0;
vector<vector<int>> f(2, vector<int>(3, 0x7fffffff));
f[0][0] = costs[0][0];
f[0][1] = costs[0][1];
f[0][2] = costs[0][2];
for (int i = 1; i < n; i++)
for (int j = 0; j < 3; j++) {
f[i&1][j] = 0x7fffffff;
for (int k = 0; k < 3; k++)
if (j != k)
f[i&1][j] = min(f[i&1][j], f[!(i&1)][k] + costs[i][j]);
}
return min(f[(n-1)&1][0], min(f[(n-1)&1][1], f[(n-1)&1][2]));
}
};
664. Counting Bits
給出一個 非負 整數 num,對所有滿足 0 ≤ i ≤ num
條件的數字 i 均需要計算其二進制表示中數字 1 的個數並以數組的形式返回。
提示:設dp[i]dp[i]表示i的二進制表示中數字1的個數。狀態轉移方程爲:dp[i]=dp[i>>1]+i\%2dp[i]=dp[i>>1]+i%2。
第二種取巧的方法是《劍指offer》中提到的i&(i-1)就是使其最右邊的1變爲0。
答案:
class Solution {
public:
/**
* @param num: a non negative integer number
* @return: an array represent the number of 1's in their binary
*/
vector<int> countBits(int num) {
int len = num + 1;
vector<int> dp(len, 0);
for(int i = 1; i <= num; ++i) {
dp[i] = dp[i>>1] + i % 2;
}
return dp;
}
};
public class Solution {
public int[] countBits(int num) {
int[] f = new int[num+1];
int i;
f[0] = 0;
for (i=1; i<=num; ++i) {
f[i] = f[i&(i-1)] + 1;
}
return f;
}
}
516. Paint House II
這裏有n
個房子在一列直線上,現在我們需要給房屋染色,共有k
種顏色。每個房屋染不同的顏色費用也不同,你需要設計一種染色方案使得相鄰的房屋顏色不同,並且費用最小。
費用通過一個n
xk
的矩陣給出,比如cost[0][0]
表示房屋0
染顏色0
的費用,cost[1][2]
表示房屋1
染顏色2
的費用。
提示:按照原先的思路時間複雜度爲O(NK^2),爲了減小時間複雜度到O(NK),我們只需記錄第i棟房子的最小值與次小值。
前者在lintcode上只能通過94%的數據。
答案:時間O(NK^2),空間O(1)。 i&1相當於將原來的行數每兩行一組, !(i&1)相當於i-1。
//O(NK^2)
class Solution {
public:
/**
* @param costs: n x k cost matrix
* @return: an integer, the minimum cost to paint all houses
*/
int minCostII(vector<vector<int>> &costs) {
// write your code here
int n = costs.size();
if (!n) return 0;
int m = costs[0].size();
vector<vector<int>> f(2, vector<int>(m, 0x7fffffff));
f[0][0] = costs[0][0];
f[0][1] = costs[0][1];
f[0][2] = costs[0][2];
for(int k = 0; k < m; k++){
f[0][k] = costs[0][k];
}
for (int i = 1; i < n; i++)
for (int j = 0; j < m; j++) {
f[i&1][j] = 0x7fffffff;
for (int k = 0; k < m; k++)
if (j != k)
f[i&1][j] = min(f[i&1][j], f[!(i&1)][k] + costs[i][j]);
}
//return min(f[(n-1)&1][0], min(f[(n-1)&1][1], f[(n-1)&1][2]));
int result = 0x7fffffff;
for(int k = 0; k < m; k++){
if(f[(n-1)&1][k] < result){
result = f[(n-1)&1][k];
}
}
return result;
}
};
時間O(NK),空間O(1)
class Solution {
public:
/**
* @param costs: n x k cost matrix
* @return: an integer, the minimum cost to paint all houses
*/
int minCostII(vector<vector<int>> &costs) {
// write your code here
int n = costs.size();
if (!n) return 0;
int m = costs[0].size();
vector<vector<int>> f(2, vector<int>(m, 0x7fffffff));
for(int k = 0; k < m; k++){
f[0][k] = costs[0][k];
}
int min1,min2,j1,j2;
for (int i = 1; i < n; i++){
min1 = min2 = 0x7fffffff;
for (int j = 0; j < m; j++) {
if(f[!(i&1)][j] < min1){
min2 = min1;
j2 = j1;
min1 = f[!(i&1)][j];
j1 = j;
}
else if(f[!(i&1)][j] < min2){
min2 = f[!(i&1)][j];
j2 = j;
}
}
for(int k = 0; k < m; k++){
if(k != j1){
f[i&1][k] = min1 + costs[i][k];
}
else{
f[i&1][k] = min2 + costs[i][k];
}
}
}
int result = 0x7fffffff;
for(int k = 0; k < m; k++){
if(f[(n-1)&1][k] < result){
result = f[(n-1)&1][k];
}
}
return result;
}
};
392. House Robber
假設你是一個專業的竊賊,準備沿着一條街打劫房屋。每個房子都存放着特定金額的錢。你面臨的唯一約束條件是:相鄰的房子裝着相互聯繫的防盜系統,且 當相鄰的兩個房子同一天被打劫時,該系統會自動報警。
給定一個非負整數列表,表示每個房子中存放的錢, 算一算,如果今晚去打劫,在不觸動報警裝置的情況下, 你最多可以得到多少錢 。
提示:線性DP題目.
設 dp[i] 表示前i家房子最多收益, 答案是 dp[n], 狀態轉移方程是
dp[i] = max(dp[i-1], dp[i-2] + A[i-1])
考慮到dp[i]的計算只涉及到dp[i-1]和dp[i-2], 因此可以O(1)空間解決.
答案:其中的取餘運算可以用幾個變量代替但是可讀性不好。
class Solution {
public:
/**
* @param A: An array of non-negative integers
* @return: The maximum amount of money you can rob tonight
*/
long long houseRobber(vector<int> &A) {
// write your code here
int n = A.size();
if(n == 0) return 0;
long long int res[2];
res[0] = A[0];
res[1] = max(A[0], A[1]);
for(int i = 2; i < n; i++){
res[i%2] = max(res[(i-1)%2], res[(i-2)%2] + A[i]);
}
return res[(n-1)%2];
}
};
534. House Robber II
在上次打劫完一條街道之後,竊賊又發現了一個新的可以打劫的地方,但這次所有的房子圍成了一個圈,這就意味着第一間房子和最後一間房子是挨着的。每個房子都存放着特定金額的錢。你面臨的唯一約束條件是:相鄰的房子裝着相互聯繫的防盜系統,且 當相鄰的兩個房子同一天被打劫時,該系統會自動報警。
給定一個非負整數列表,表示每個房子中存放的錢, 算一算,如果今晚去打劫,在不觸動報警裝置的情況下, 你最多可以得到多少錢 。
提示:在之前的基礎上分兩種情況,要麼偷第一個房子不偷最後一個,要麼偷最後一個不偷第一個。兩個都不偷的情況包含在第 二種情況。 如果不另外寫一個計算函數的話,一定要注意兩種情況的初始條件。
答案:
class Solution {
public:
/**
* @param nums: An array of non-negative integers.
* @return: The maximum amount of money you can rob tonight
*/
int houseRobber2(vector<int> &nums) {
// write your code here
int n = nums.size();
if(n == 0) return 0;
if(n == 1) return nums[0];
if(n == 2) return max(nums[0], nums[1]);
long long int res[2];
//condition 1
res[0] = nums[0];
res[1] = max(nums[0], nums[1]);
for(int i = 2; i < n - 1; i++){
res[i%2] = max(res[(i-1)%2], res[(i-2)%2] + nums[i]);
}
long long int tmpMax = res[(n-2)%2];
//condition 2
res[0] = 0;
res[1] = nums[1];
for(int i = 2; i < n; i++){
res[i%2] = max(res[(i-1)%2], res[(i-2)%2] + nums[i]);
}
tmpMax = max(tmpMax, res[(n-1)%2]);
return tmpMax;
}
};
149. Best Time to Buy and Sell Stock
假設有一個數組,它的第i個元素是一支給定的股票在第i天的價格。如果你最多隻允許完成一次交易(例如,一次買賣股票),設計一個算法來找出最大利潤。
提示:記錄兩個值,一個是目前爲止的最小值,目前爲止的最大利潤。最大利潤爲min之後的最大值減去min。
答案:
class Solution {
public:
/**
* @param prices: Given an integer array
* @return: Maximum profit
*/
int maxProfit(vector<int> &prices) {
// write your code here
if(prices.size() == 0){
return 0;
}
int min = 0x7fffffff;
int profit = 0;
for(int i : prices){
min = i < min ? i : min;
profit = (i - min) > profit ? i - min : profit;
}
return profit;
}
};
150. Best Time to Buy and Sell Stock II
給定一個數組 prices
表示一支股票每天的價格.
你可以完成任意次數的交易, 不過你不能同時參與多個交易 (也就是說, 如果你已經持有這支股票, 在再次購買之前, 你必須先賣掉它).
設計一個算法求出最大的利潤.
提示:這個最簡單,只要明天比今天價格高就買入。
答案:
class Solution {
public:
/**
* @param prices: Given an integer array
* @return: Maximum profit
*/
int maxProfit(vector<int> &prices) {
int n = prices.size();
int profit = 0;
for (int i = 1; i < n; i++) {
if (prices[i] - prices[i - 1] > 0) {
profit += prices[i] - prices[i - 1];
}
}
return profit;
}
};
151. Best Time to Buy and Sell Stock III
假設你有一個數組,它的第i個元素是一支給定的股票在第i天的價格。設計一個算法來找到最大的利潤。你最多可以完成兩筆交易。
提示:分爲五種情況。前面的題基本用座標型來寫的,此題用序列型來寫比較好,前0天只能處於階段1。當天買的不計算紅利。
最下面的式子有問題,最後一項無意義可刪除,因爲其賣了接着買相當於一次買賣。
答案:
class Solution {
public:
/**
* @param prices: Given an integer array
* @return: Maximum profit
*/
const int MinValue = 0x80000000;
int maxProfit(vector<int> &prices) {
// write your code here
int n = prices.size();
if(n == 0) return 0;
vector<vector<int>> dp(n + 1,vector<int>(5 + 1));
for(int k = 1; k <= 5; k++){
dp[0][k] = MinValue;
}
dp[0][1] = 0;
for(int i = 1; i <= n; i++){
//手中未持有股票
for(int j = 1; j <= 5; j+=2){
//前一天也未持有股票
dp[i][j] = dp[i-1][j];
if(j > 1 && i > 1 && dp[i-1][j-1] != MinValue){
// 前一天持有,今天獲得紅利
dp[i][j] = max(dp[i][j], dp[i-1][j-1] + prices[i-1] - prices[i-2]);
}
}
//手中持有股票
for(int j = 2; j <= 5; j+=2){
//前一天未持有股票今天買入
dp[i][j] = dp[i-1][j-1];
if(i > 1 && dp[i-1][j] != MinValue){
// 前一天持有股票今天獲得紅利
dp[i][j] = max(dp[i][j], dp[i-1][j] + prices[i-1] - prices[i-2]);
}
}
}
int res = 0;
for (int j = 1; j <= 5; j += 2) {
res = max(res, dp[n][j]);
}
return res;
}
};
393. Best Time to Buy and Sell Stock IV
提示:如果K > N/2,那麼相當於BTBSS II,否則將 III 擴展到 2K+1,五種階段不變。
答案:
class Solution {
public:
/**
* @param K: An integer
* @param prices: An integer array
* @return: Maximum profit
*/
int maxProfit(int K, vector<int> &prices) {
// write your code here
const int MinValue = 0x80000000;
int n = prices.size();
if(n == 0) return 0;
int res = 0;
//相當於任意次買賣
if(K > n/2){
for (int i = 1; i < n; i++) {
if (prices[i] - prices[i - 1] > 0) {
res += prices[i] - prices[i - 1];
}
}
return res;
}
vector<vector<int>> dp(n + 1,vector<int>(2*K + 2));
for(int k = 1; k <= 2*K + 1; k++){
dp[0][k] = MinValue;
}
dp[0][1] = 0;
for(int i = 1; i <= n; i++){
//手中未持有股票
for(int j = 1; j <= 2*K + 1; j+=2){
//前一天也未持有股票
dp[i][j] = dp[i-1][j];
if(j > 1 && i > 1 && dp[i-1][j-1] != MinValue){
// 前一天持有,今天獲得紅利
dp[i][j] = max(dp[i][j], dp[i-1][j-1] + prices[i-1] - prices[i-2]);
}
}
//手中持有股票
for(int j = 2; j <= 2*K + 1; j+=2){
//前一天未持有股票今天買入
dp[i][j] = dp[i-1][j-1];
if(i > 1 && dp[i-1][j] != MinValue){
// 前一天持有股票今天獲得紅利
dp[i][j] = max(dp[i][j], dp[i-1][j] + prices[i-1] - prices[i-2]);
}
}
}
for (int j = 1; j <= 2*K + 1; j += 2) {
res = max(res, dp[n][j]);
}
return res;
}
};
76. Longest Increasing Subsequence
給定一個整數序列,找到最長上升子序列(LIS),返回LIS的長度。
提示:此題用動態規劃時間複雜度爲O(N^2),O(NlogN)的二分查找方法放在第七講。動態規劃的思路是建立數組,f[i]表示以i爲結尾的最長子序列,轉移方程:Dp[i] = max{Dp[j]} + 1
答案:
class Solution {
public:
/**
* @param nums: The integer array
* @return: The length of LIS (longest increasing subsequence)
*/
int longestIncreasingSubsequence(vector<int> nums) {
int f[nums.size()];
int max = 0;
for (int i = 0; i < nums.size(); i++) {
f[i] = 1;
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i]) {
f[i] = f[i] > f[j] + 1 ? f[i] : f[j] + 1;
}
}
if (f[i] > max) {
max = f[i];
}
}
return max;
}
};
602. Russian Doll Envelopes
給一定數量的信封,帶有整數對 (w, h)
分別代表信封寬度和高度。一個信封的寬高均大於另一個信封時可以放下另一個信封。
求最大的信封嵌套層數。
提示:此題目用動態規劃做時間複雜度也是O(N^2)。使用二分法優化的方法後面再講。
信封存在兩個維度,首先貪心按照其中一個維度將信封排序,然後在另一個維度上面尋找最長上升子序列。
如排序按照長度從小到大,同樣長度按照寬度從大到小排序
答案:
bool cmp(const pair<int,int>&x, const pair<int, int>&y) {
return x.first != y.first ? x.first < y.first : x.second > y.second;
}
class Solution {
public:
/*
* @param envelopes: a number of envelopes with widths and heights
* @return: the maximum number of envelopes
*/
int maxEnvelopes(vector<pair<int, int>>& envelopes) {
// write your code here
int n = envelopes.size();
if (n == 0) {
return 0;
}
sort(envelopes.begin(), envelopes.end(), cmp);
vector<int> f(n);
int i, j, res = 0;
for(i = 0; i < n; i++){
f[i] = 1;
for(j = 0; j < i; j++){
if(envelopes[j].first < envelopes[i].first && envelopes[j].second < envelopes[i].second){
f[i] = max(f[i], f[j] + 1);
}
}
res = max(res, f[i]);
}
return res;
}
};