动态规划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)

猜你喜欢

转载自www.cnblogs.com/JCcodeblgos/p/11456087.html