1. 程式人生 > >動態規劃(Dynamic Programming)問題集錦(二)

動態規劃(Dynamic Programming)問題集錦(二)

資料結構與演算法中動態規劃問題的總結歸納。

Maximum Subarray

題目描述:Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array [-2,1,-3,4,-1,2,1,-5,4],the contiguous subarray [4,-1,2,1] has the largest sum = 6.LeetCode

int maxSubArray(vector
<int>
& nums) { int maxSoFar = nums[0], maxEndingHere = nums[0]; for (int i = 1;i < nums.size(); i++) { maxEndingHere = max(maxEndingHere + nums[i], nums[i]); maxSoFar = max(maxSoFar, maxEndingHere); } return maxSoFar; }

Best Time to Buy and Sell Stock

題目描述:Say you have an array for which the ith element is the price of a given stock on day i.If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.
Example 1: Input: [7, 1, 5, 3, 6, 4], Output: 5
max. difference = 6-1 = 5 (not 7-1 = 6, as selling price needs to be larger than buying price)
Example 2: Input: [7, 6, 4, 3, 1], Output: 0
In this case, no transaction is done, i.e. max profit = 0.

LeetCode

//minVal表示已遍歷元素中的最小值,profit表示當前收益的最大值
int maxProfit(vector<int>& prices) 
{
    int minVal = INT_MAX, profit = 0, size = prices.size();
    for(int i = 0; i < size; i++)
    {
        minVal = min(prices[i], minVal);
        profit = max(profit, prices[i] - minVal);
    }
    return profit;
}

/**
轉化為最大子數列問題(Kadane演算法),從動態規劃的角度考慮:
maxEndingHere[i] = max(0, maxEndingHere[i - 1] + prices[i] - prices[i - 1]),
maxEndingHere[i - 1]表示第i-1天售出股票的最大利潤,maxEndingHere[i]表示第i天售出股票的最大利潤,
因此需要加上前後兩天的股票差價,maxSoFor表示到目前為止的最大利益(不一定在當天售出)
*/
int maxProfit(vector<int>& prices) 
{
    int maxEndingHere = 0, maxSoFar = 0, size = prices.size();
    for(int i = 1; i < size; i++)
    {
        maxEndingHere = max(0, maxEndingHere += prices[i] - prices[i - 1]);
        maxSoFar = max(maxEndingHere, maxSoFar);
    }
    return maxSoFar;
}

Best Time to Buy and Sell Stock II

題目描述:Say you have an array for which the ith element is the price of a given stock on day i.Design an algorithm to find the maximum profit. You may complete as many transactions as you like(ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).LeetCode

//累加相鄰極小值點與極大值點之間的差值
int maxProfit(vector<int>& prices) 
{
    int minVal = INT_MAX, maxPro = 0, sumPro = 0, size = prices.size();
    if(size == 0)   return 0;
    for(int i = 0; i < prices.size(); i++)
    {
        minVal = min(minVal, prices[i]);
        if(maxPro < prices[i] - minVal)
            maxPro = prices[i] - minVal;
        else
        {
            sumPro += maxPro;   //maxPro沒有更新,說明前一個點為極值點,累加收益值,並更新最小值和單次最大收益
            minVal = prices[i];
            maxPro = 0;
        }
    }
    if(minVal != prices[size - 1])  //考慮最後一個點為極大值的情況
        sumPro += maxPro;
    return sumPro;
}

//累加遞增的股票差值
int maxProfit(vector<int>& prices) 
{
    int profit = 0;
    for(int i = 1; i < prices.size(); i++)
    {
        if(prices[i - 1] < prices[i])
            profit += prices[i] - prices[i - 1];
    }
    return profit;
}

Best Time to Buy and Sell Stock III

題目描述:Say you have an array for which the ith element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete at most two transactions.
Note: You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

/**DP演算法:可擴充套件到最多交易k次
定義local[i][j]為在到達第i天時最多可進行j次交易並且最後一次交易在最後一天賣出的最大利潤,此為區域性最優。
定義global[i][j]為在到達第i天時最多可進行j次交易的最大利潤,此為全域性最優。遞推式為:
diff = prices[i] - prices[i - 1]
local[i][j] = max(global[i - 1][j - 1] + max(diff, 0), local[i - 1][j] + diff)
global[i][j] = max(local[i][j], global[i - 1][j])
其中區域性最優值是比較前一天並少交易一次的全域性最優加上大於0的差值,和前一天的區域性最優加上差值中取較大值,
而全域性最優比較區域性最優和前一天的全域性最優。
*/
int maxProfit(vector<int>& prices) 
{
    if(prices.empty())  return 0;
    int n = prices.size();
    vector<vector<int>> local(n, vector<int>(3, 0)), global(n, vector<int>(3, 0));
    for(int i = 1; i < n; i++)
    {
        int diff = prices[i] - prices[i - 1];
        for(int j = 1; j < 3; j++)
        {
            local[i][j] = max(global[i - 1][j - 1] + max(diff, 0), local[i - 1][j] + diff); 
            global[i][j] = max(global[i - 1][j], local[i][j]);
        }
    }
    return global[n - 1][2];
}

/**
用一維陣列來代替二維陣列,可以極大地減少空間,由於覆蓋的順序關係,遍歷j需要從2到1,
這樣可以取到正確的g[j-1]值,而非已經被覆蓋過的值
*/
int maxProfit(vector<int>& prices) 
{
    if(prices.empty())  return 0;
    int n = prices.size();
    int local[3] = { 0 }, global[3] = { 0 };
    for(int i = 1; i < n; i++)
    {
        int diff = prices[i] - prices[i - 1];
        for(int j = 2; j >= 1; j--)
        {
            local[j] = max(global[j - 1] + max(0, diff), local[j] + diff);
            global[j] = max(global[j], local[j]);
        }
    }
    return global[2];
}

/**
lowestBuyPrice1始終為輸入陣列中的最低價,maxProfit1記錄到目前為止最高價與最低價的差價(最高價需在最低價後面),
lowestBuyPrice2為相對最小值,maxProfit2則為最多買賣兩次的累積最大收益
*/
int maxProfit(vector<int>& prices) 
{
    int maxProfit1 = 0, maxProfit2 = 0, lowestBuyPrice1 = INT_MAX, lowestBuyPrice2 = INT_MAX;
    for(int p : prices)
    {
        maxProfit2 = max(maxProfit2, p - lowestBuyPrice2);
        lowestBuyPrice2 = min(lowestBuyPrice2, p - maxProfit1);
        maxProfit1 = max(maxProfit1, p - lowestBuyPrice1);
        lowestBuyPrice1 = min(lowestBuyPrice1, p);
    }
    return maxProfit2;
}

triangle

問題描述:Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).
Note: Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.LeetCode

/**
DP演算法:從下往上遍歷,對當前的某一行取值,選擇其下一行相鄰的兩個累加路徑和中較小的一個,進行求和並將結果作為當前行當前位置的最短路徑和。
*/
int minimumTotal(vector<vector<int>>& triangle) 
{
    if(triangle.empty())    return 0;
    int size = triangle.size(), minSum = 0;
    vector<int> pathSum(triangle[size - 1].begin(), triangle[size - 1].end());
    for(int i = size - 2; i >= 0; i--)
    {
        for(int j = 0; j < i + 1; j++)
        {
            pathSum[j] = triangle[i][j] + min(pathSum[j], pathSum[j + 1]);
        }
    }
    return pathSum[0];
}