• 动态规划3 序列型动态规划


    序列型动态规划

    动态规划dp[i]中的下标i表示前i个元素a[0],a[1],...,a[i-1]的某种性质

    初始化中,dp[0]表示空序列的性质

    坐标型动态规划的初始条件dp[0]就是指以a[0]为结尾的自子序列的性质

    题目1:LintCode 516 Paint House II

    dp[i][1]....dp[i][k]  :尤其前i栋房子 并且i-1是颜色1~k  的最小花费

    dp[i][1] = min{dp[i-1][2]+cost[i-1][1],.....,dp[i-1][k]+cost[i-1][1]}

    dp[i][2] = min{dp[i-1][1]+cost[i-1][2],.....,dp[i-1][k]+cost[i-1][2]}

    ...

    dp[i][k] = min{dp[i-1][1]+cost[i-1][k],.....,dp[i-1][k-1]+cost[i-1][k]}

    dp[i][j] = min k≠j {dp[i-1][k]} + cost[i-1][j];

    i从0到N

    j从1到k

    k从1到k

    算法的时间复杂度:O(NK^2)

    加快计算:

    dp[i][j] = min k≠j {dp[i-1][k]} + cost[i-1][j];

    min k≠j {dp[i-1][k]} :每次需要求f[i-1][1],...,f[i-1][k]中出了一个元素之外其他元素的最小值

    重复计算

    抽象:求出除了一个元素的之外的序列中的最小值

    比如序列:95 88 75 98 90

    75<88<90<95<95

    最小值、最小值

    在线维护最小值、次小值,就两个值。

    假如最小值dp[i-1][a],次小值为dp[i-1][b]

    则对于j=1,2,3,...,a-1,a+1,...,k   dp[i][j] = dp[i][a]+cost[i-1][j];

    dp[i][a] = dp[i-1][b] + cost[i-1][a];

    从此时间复杂度就降维O(NK)

     1 class Solution {
     2 public:
     3     /**
     4      * @param costs: n x k cost matrix
     5      * @return: an integer, the minimum cost to paint all houses
     6      */
     7     int minCostII(vector<vector<int>> &A) {
     8         // write your code here
     9         int n = A.size();
    10         if(n==0)
    11             return 0;
    12         
    13         const int INF = 0x3f3f3f3f;
    14         
    15         int k = A[0].size();
    16         
    17         int dp[n+1][k];  // 注意数组申请
    18         int min1, min2;
    19         int j1, j2;
    20         
    21         // 初始化
    22         for(int j=0; j<k; j++){  // dp数组第一列初始化为0
    23             dp[0][j] = 0;
    24         }
    25         
    26         // 从1开始 第一个房子 到 第n个房子
    27         for(int i=1; i<=n; i++){
    28             
    29             // 求最小值和次小值 每一次都要求
    30             min1 = min2 = INF;
    31             for(int j=0; j<k; j++){
    32                 if(dp[i-1][j] < min1){
    33                     // 把最小值给次小值
    34                     min2 = min1;
    35                     j2 = j1;
    36                     min1 = dp[i-1][j];
    37                     j1 = j;
    38                 }else{  // 这里也可以用else if
    39                     if(dp[i-1][j] < min2){
    40                         min2 = dp[i-1][j];
    41                         j2 = j;
    42                     }
    43                 }
    44             }
    45             
    46             // dp计算
    47             for(int j=0; j<k; j++){
    48                 if(j != j1){
    49                     dp[i][j] = dp[i-1][j1] + A[i-1][j];
    50                 }else{
    51                     // j == j1
    52                     dp[i][j] = dp[i-1][j2] + A[i-1][j];
    53                 }
    54             }
    55         }
    56         
    57         int res = INF;
    58         for(int j=0; j<k; j++){
    59             res = min(res, dp[n][j]);
    60         }
    61         
    62         return res;
    63         
    64     }
    65 };

    题目2:392 House Robber

    https://www.lintcode.com/problem/house-robber/description

    不能偷挨着的两家邻居

    确定状态:

    最后一个房子 偷 或者 不偷

    偷n-1房子:需要知道前n-1栋房子中最大能偷多少金币

    不偷n-1房子:前n-1房子的最优策略

    (两种状态)

    dp[i][0]  不偷

    dp[i][1] 偷

    • dp[i][0] = max {dp[i-1][0], dp[i-1][1]}
    • dp[i][1] = dp[i-1][0] + A[i-1]   偷i,只能选择不偷i-1

    ---------------------------------------------------------------------------------------------

    简化:在不偷房子i-1的前提下,前i栋房子中最大能偷度多少金币,其实就是前i-1栋房子能投多少金币

    dp[i] 为窃贼在前i栋房子最多能投多少金币。

    dp[i] = max {dp[i-1], dp[i-2]+A[i-1]}

    初始条件:dp[0] = 0 没房子,偷0枚金币    序列性动态规划0就是空

    dp[1] = A[0]

    dp[2] = max{A[0],A[1]}

     1 class Solution {
     2 public:
     3     /**
     4      * @param A: An array of non-negative integers
     5      * @return: The maximum amount of money you can rob tonight
     6      */
     7     long long houseRobber(vector<int> &A) {
     8         // write your code here
     9         int n = A.size();
    10         if(n==0){
    11             return 0;
    12         }
    13         long dp[n+1];
    14         dp[0] = 0;
    15         dp[1] = A[0];
    16         for(int i=2; i<=n; i++){
    17             dp[i] = max(dp[i-1], dp[i-2]+A[i-1]);
    18         }
    19         
    20         return dp[n];
    21         
    22     }
    23 };

    题目3:LintCode 534 House Robber || 

    https://www.lintcode.com/problem/house-robber-ii/description

    上题的房子换成了圈,房子0和房子n-1变成邻居,不能同时偷盗。

    分情况讨论

    情况1:没偷档子0       最优策略就是窃贼对于房子1~N-1的最优策略->化为House Robber

    情况2:没偷档子n-1    最优策略就是窃贼对于房子0~N-2的最优策略->化为House Robber

     1 class Solution {
     2 public:
     3     /**
     4      * @param nums: An array of non-negative integers.
     5      * @return: The maximum amount of money you can rob tonight
     6      */
     7     
     8     int dp_hr(int A[], int n){
     9         int dp[n+1];
    10         dp[0] = 0;
    11         dp[1] = A[0];
    12         for(int i=2; i<=n; i++){
    13             dp[i] = max(dp[i-1], dp[i-2]+A[i-1]);
    14         }
    15         return dp[n];
    16     }
    17     
    18     int houseRobber2(vector<int> &A) {
    19         // write your code here
    20         
    21         int n = A.size();
    22         if(n==0){
    23             return 0;
    24         }
    25         
    26         if(n==1){  // 注意下面切分数组的时候考虑当数组长度为1,是切分不到东西的。
    27             return A[0];
    28         }
    29         
    30         int A_0[n-1];
    31         for(int i=0; i<n-1; i++){
    32             A_0[i] = A[i];
    33         }
    34         
    35         int ans = dp_hr(A_0, n-1);
    36         
    37         
    38         
    39         int A_1[n-1];
    40         for(int i=1; i<n; i++){
    41             A_1[i-1] = A[i];  // 注意这边减一
    42         }
    43         ans = max(ans, dp_hr(A_1, n-1));
    44         
    45         return ans;
    46     }
    47 };

    题目4:LintCode 149 Best Time To Buy And Sell Stock

    https://www.lintcode.com/problem/best-time-to-buy-and-sell-stock/description

    [3,2,3,1,2]

    2买入3卖出

    先买后买

    保底策略:

     1 class Solution {
     2 public:
     3     /**
     4      * @param prices: Given an integer array
     5      * @return: Maximum profit
     6      */
     7     int maxProfit(vector<int> &A) {
     8         // write your code here
     9         
    10         int min_a = A[0];
    11         int ans = 0;
    12         for(int i=0; i<A.size(); i++){
    13             int temp = A[i];
    14             if(temp-min_a > ans){
    15                 ans = temp-min_a;
    16             }
    17             min_a = min(min_a, temp);
    18         }
    19         return ans;
    20     }
    21 };

    题目5:LintCode 150 Best Time To Buy And Sell Stock ||

    任意多次买卖,但是任意时刻手中醉倒持有一股。

    解法:贪心

    买卖一个上升策略

     1 class Solution {
     2 public:
     3     /**
     4      * @param prices: Given an integer array
     5      * @return: Maximum profit
     6      */
     7     int maxProfit(vector<int> &a) {
     8         // write your code here
     9         
    10         int sum = 0;
    11         int per_satrt = 0, index =0;
    12         int per_end = 0;
    13         if(a.size()==0){
    14             return 0;
    15         }
    16         // 贪心策略
    17         // if(a.size() < 2){
    18         //     return 0;
    19         // }else if(a.size() == 2){
    20         //     if(a[0] < a[1]){
    21         //         return a[1]-a[0];
    22         //     }else{
    23         //         return 0;
    24         //     }
    25         // }
    26         for(int  i=1; i<a.size(); i++){
    27             if(a[i] >= a[i-1]){
    28                 per_end = i;
    29             }else{
    30                 sum = sum + (a[per_end] - a[per_satrt]);
    31                 // 清空变量 要不然会多加
    32                 per_satrt = i;
    33                 per_end = i;
    34             }
    35         }
    36         sum = sum + (a[per_end] - a[per_satrt]);
    37         
    38         return sum;
    39     }
    40 };

    其实只要记录相邻两天的差值大于0就可,就可以得到上升子序列的所有和。

     1 class Solution {
     2 public:
     3     /**
     4      * @param prices: Given an integer array
     5      * @return: Maximum profit
     6      */
     7     int maxProfit(vector<int> &a) {
     8         // write your code here
     9         
    10         if(a.size() == 0){
    11             return 0;
    12         }
    13         
    14         int res = 0;
    15         
    16         for(int i=0; i<a.size()-1; i++){
    17             if(a[i+1]-a[i] > 0){
    18                 res += a[i+1]-a[i];
    19             }
    20         }
    21         
    22         return res;
    23     }
    24 };

    题目5:LintCode 150 Best Time To Buy And Sell Stock |||

    https://www.lintcode.com/problem/best-time-to-buy-and-sell-stock-iii/description

    醉倒两次买了 两次卖,每次都是一只股

    • 买过
    • 没买过
    • 买多少次

    把买卖股票的过程划分:

    阶段1   第一次买 阶段2  第一次卖  阶段3  第二次买 阶段4  第二次卖 阶段5

    当股票在阶段5的时候就不能买了,阶段三是可以买卖依次,阶段四继续持有

    dp[i][j]: 前i天结束后,在阶段j的最大获利。

    阶段1,3,5 ---- 手中无股票

    dp[i][j] = max{dp[i-1][j], dp[i-1][j-1]+p_i-1-p_i-2}

    昨天没有持有股票             昨天持有股票,今天卖出清仓

    阶段2,4   ---- 手中有股票

    dp[i][j] = max{dp[i-1][j]+p_i-1-p_i-2, dp[i][j], dp[i-1][j-1]+p_i-1-p_i-2}

    昨天就持有股票,继续持有并获利                昨天没有持有股票,今天买入              昨天持有上一次买的股票,今天买入并立即买入

    模拟的过程:求序列的上升最大值,然后给每个子序列排序,选出2个值,这个过程的时间复杂度是O(NlongN)

  • 相关阅读:
    hdu In Action
    hdu Bus Pass
    acm steps chapter6总结
    原创:扫雷游戏
    排列组合算法
    原创:泛型方法例子
    原创:QQ群发言统计
    菜鸟也做有道难题①
    查询时带Ntext或nvarchar(Max)到底有多影响速度
    使用正则要注意的几点
  • 原文地址:https://www.cnblogs.com/JCcodeblgos/p/11456087.html
Copyright © 2020-2023  润新知