动态规划2 坐标型动态规划

 

题目1

https://www.lintcode.com/problem/unique-paths-ii/description

 1 class Solution {
 2 public:
 3     /**
 4      * @param obstacleGrid: A list of lists of integers
 5      * @return: An integer
 6      */
 7     int uniquePathsWithObstacles(vector<vector<int>> &obstacleGrid) {
 8         // write your code here
 9         int m = obstacleGrid.size();
10         int n = obstacleGrid[0].size();
11         
12         if(m==0 || n==0){
13             return 0;
14         }
15         
16         int dp[m][n];
17         
18         /*
19         // 数组使用new
20         int **dp = new int *[n];
21         for(int i=0; i<m; i++)
22             dp[i] = new int[m];
23         */
24         
25         // 初始化
26         if(obstacleGrid[0][0]==1){
27             dp[0][0] = 0;
28         }else{
29             dp[0][0] = 1;
30         }
31         
32         for(int i=1; i<m; i++){
33             if(obstacleGrid[i][0]==1){
34                 dp[i][0] = 0;
35             }else{
36                 dp[i][0] = 0;
37                 dp[i][0] = dp[i][0]+ dp[i-1][0];
38             }
39         }
40         for(int j=1; j<n; j++){
41             if(obstacleGrid[0][j]==1){
42                 dp[0][j] = 0;
43             }else{
44                 dp[0][j] = 0;
45                 dp[0][j] = dp[0][j] + dp[0][j-1];
46             }
47         }
48         
49         for(int i=1; i<m; i++){
50             for(int j=1; j<n; j++){
51                 if(obstacleGrid[i][j]==1){
52                     dp[i][j] = 0;
53                 }else{
54                     dp[i][j] = dp[i-1][j] + dp[i][j-1];
55                 }
56             }
57         }
58         
59         return dp[m-1][n-1];
60     }
61 };

 

代码对比

 

class Solution {
public:
    /**
     * @param obstacleGrid: A list of lists of integers
     * @return: An integer
     */
    int uniquePathsWithObstacles(vector<vector<int>> &obstacleGrid) {
        // write your code here
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        
        if(m==0 || n==0){
            return 0;
        }
        
        int dp[m][n];
        
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                if(obstacleGrid[i][j] == 1){
                    dp[i][j] = 0;
                }else{
                    if(i==0 && j==0){  // 遇见石头直接返回0,其后一个数字等于前一个数组加这个数组,所以第一行和第一列不全是1
                        dp[i][j] = 1;
                    }else{
                        dp[i][j]=0;
                        if(i-1>=0){  // 有上边
                            dp[i][j] += dp[i-1][j];
                        }
                        
                        if(j-1>=0){  // 有左边
                            dp[i][j] += dp[i][j-1];
                        }
                    }
                }
            }
        }
        
        return dp[m-1][n-1];
    }
};

 

题目2:LintCode 515 Paint House

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

 

序列+状态

 

 1 class Solution {
 2 public:
 3     /**
 4      * @param costs: n x 3 cost matrix
 5      * @return: An integer, the minimum cost to paint all houses
 6      */
 7     
 8     // 不用写min函数 
 9     // int min(int x, int y){
10     //     if(x<y){
11     //         return x;
12     //     }
13     //     return y;
14     // }
15     
16     
17     // 当我们不知道如何选择状态的时候,就要用开辟数组来记录。
18     int minCost(vector<vector<int>> &costs) {
19         // write your code here
20         int n = costs.size();
21         if(n==0){
22             return 0;
23         }
24         
25         // const int INF = 0x3f3f3f3f;
26         
27         int dp[n+1][3];   // dp[i][j] 表示油漆前i个房子并且房子i-1是红色、蓝色、绿色的最小花费分别为dp[i][0] dp[i][1] dp[i][2]
28         
29         dp[0][0] = dp[0][1] = dp[0][2] = 0;
30         
31         for(int i=1; i<=n; i++){  // i表示房子的序号
32         
33             // 当第i个房子选择红色的时候,最前i个房子的最小值
34             // dp[i][0] = INF;  
35             dp[i][0] = min(dp[i-1][1]+costs[i-1][0], dp[i-1][2]+costs[i-1][0]);
36             
37             // 当第i个房子选蓝色的时候,最前i个房子的最小值
38             // dp[i][1] = INF;  // 初始化
39             dp[i][1] = min(dp[i-1][0]+costs[i-1][1], dp[i-1][2]+costs[i-1][1]);
40             
41             // 当第i个房子选择绿色的时候,最前i个房子的最小值
42             // dp[i][2] = INF;  // 初始化
43             dp[i][2] = min(dp[i-1][0]+costs[i-1][2], dp[i-1][1]+costs[i-1][2]);
44         }
45         
46         int res = min(dp[n][0], dp[n][1]);
47         res =  min(res, dp[n][2]);
48         
49         return res;
50     }
51 };

 

题目3:LintCode 512 Decode Ways

https://www.lintcode.com/problem/decode-ways/description

解密字符串,划分型。

最后一个字母,考虑是否和前面一直字母进行合并处理,此时就有了两种解密方式

即: 前n-1n-2字符的解密方式  考虑着两种方式。

设字符串s前i个字符解密成字母串有f[i]中方式

dp[i] = dp[i-1] + dp[i-2]

初始条件:dp[0]=1 空串有一种方式解密                        最值就是dp[0]=0

 1 class Solution {
 2 public:
 3     /**
 4      * @param s: a string,  encoded message
 5      * @return: an integer, the number of ways decoding
 6      */
 7     int numDecodings(string &s) {
 8         // write your code here
 9         int n = s.length();
10         if(n == 0){
11             return 0;
12         }
13         
14         int dp[n+1];  // 0~n 前i个 数组是从0~n-1的
15         dp[0] = 1; //有多少种方式 只有在变化的时候才起+1
16         
17         for(int i=1; i<=n; i++){
18             dp[i] = 0;
19             
20             int tmp = s[i-1] - '0';
21             if(tmp>=1 && tmp<=9){
22                 dp[i] += dp[i-1];
23             }
24             
25             if(i>=2){  // 控制数组不用越界
26                 tmp = (s[i-2]-'0')*10 + (s[i-1]-'0');
27                 if(tmp>=10 && tmp<=26){
28                     dp[i] += dp[i-2];
29                 }
30             }
31             
32         }
33         
34         return dp[n];
35     }
36 };

 

https://www.nowcoder.com/practice/b83b126603dd4e63bc4287d32d754886?tpId=98&tqId=32868&tPage=3&rp=3&ru=/ta/2019test&qru=/ta/2019test/question-ranking

 

牛客笔试题目:解码方式

 1 #include <iostream>
 2 #include <cstring>
 3 
 4 using namespace std;
 5 
 6 int main(){
 7     // freopen("test.txt", "r", stdin);
 8     string s;
 9     cin >> s;
10     // cout << s << endl;
11     int n = s.length();
12     
13     int dp[n+1];  // dp[i]前i个子串解码的方式
14     memset(dp, 0, sizeof(dp));
15     dp[0] = 1;
16     for(int i=1; i<=n; i++){
17         
18         int num = s[i-1]-'0';
19         if(num>=1 && num<=9){
20             dp[i] += dp[i-1];
21         }
22         
23         if(i>=2){
24             num = (s[i-2]-'0')*10 + (s[i-1]-'0');
25             if(num>=10 && num<=26){
26                 dp[i] = dp[i] + dp[i-2];
27             }
28         }
29     }
30     
31     cout << dp[n] << endl;
32     return 0;
33 }

 

牛客笔试:跳格子

状态:dp[i] 表示调到第i个格子的方式

初始条件:dp[1] = 1 格子数从1到n;当只有一个格子的时候条的方法数也就1; 这里dp[0]=1是为了计算方便。

 1 #include <iostream>
 2 #include <cstring>
 3 
 4 using namespace std;
 5 
 6 int main(){
 7     // freopen("test.txt", "r", stdin);
 8     int n;
 9     cin >> n;
10     int dp[n+1];
11     memset(dp, 0, sizeof(dp));
12     dp[0] = 1;
13     for(int i=1; i<=n; i++){
14         dp[i] += dp[i-1];
15         if(i>=2)
16             dp[i] += dp[i-2];
17     }
18     cout << dp[n] << endl;
19     return 0;
20 }

 

 

题目4:LintCode 397 最长连续单调子序列

https://www.lintcode.com/problem/longest-continuous-increasing-subsequence/description

 

确定状态:a[j]=1.

f[j]:以j结尾的最长上升子序列长度

f[j] = max{1, f[j-1]+1|j>0 and }

答案不一定是f[n]   max{dp[0], dp[1], ..., dp[n]}

时间复杂度O(n)   空间复杂度O(n)

dp[i] 表示以a[i]结尾的最长连续上升子序列的长度

 

 1 class Solution {
 2 public:
 3     /**
 4      * @param A: An array of Integer
 5      * @return: an integer
 6      */
 7     int result = 0;
 8     
 9     // 注意这里传入的是向量数组而不是单个数组
10     void calc(vector<int> &A, int n){
11         int dp[n];
12         for(int i=0; i<n; i++){
13             dp[i] = 1;  // 初始化
14             
15             if(i>0 && A[i-1]<A[i]){
16                 dp[i] = dp[i-1]+1;
17             }
18             
19             if(dp[i] > result){
20                 result = dp[i];
21             }
22         }
23     }
24     
25     int longestIncreasingContinuousSubsequence(vector<int> &A) {
26         // write your code here
27         int n = A.size();
28         if(n==0){
29             return 0;
30         }
31         
32         calc(A, n);
33         // 反转A
34         int i, j, t;
35         i = 0;
36         j= n-1;
37         while(i<j){
38             t = A[i];
39             A[i] = A[j];
40             A[j] = t;
41             i++;
42             j--;
43         }
44         
45         
46         calc(A, n);
47         
48         return result;
49     }
50 };

 

 

空间复杂度O(1)  滚动数组

 1 class Solution {
 2 public:
 3     /**
 4      * @param A: An array of Integer
 5      * @return: an integer
 6      */
 7     int result = 0;
 8     
 9     // 注意这里传入的是向量数组而不是单个数组
10     void calc(vector<int> &A, int n){
11         int old, now = 0;
12         int dp[2];
13         
14         for(int i=0; i<n; i++){
15             old = now;
16             now = 1-now; // 在0,1,0,1之间徘徊
17             
18             dp[now] = 1; // // 初始化  数组从1开始 0,1,0,1处理
19             
20             if(i>0 && A[i-1]<A[i]){
21                 dp[now] = dp[old]+1;
22             }
23             
24             if(dp[now] > result){
25                 result = dp[now];
26             }
27         }
28     }
29     
30     int longestIncreasingContinuousSubsequence(vector<int> &A) {
31         // write your code here
32         int n = A.size();
33         if(n==0){
34             return 0;
35         }
36         
37         calc(A, n);
38         // 反转A
39         int i, j, t;
40         i = 0;
41         j= n-1;
42         while(i<j){
43             t = A[i];
44             A[i] = A[j];
45             A[j] = t;
46             i++;
47             j--;
48         }
49         
50         
51         calc(A, n);
52         
53         return result;
54     }
55 };

 

 

牛客题目:回文字符串

https://www.nowcoder.com/practice/5bfb74efcd5449e69a480550b1fef431?tpId=98&tqId=32846&tPage=1&rp=1&ru=/ta/2019test&qru=/ta/2019test/question-ranking

设dp[i]:表示以a[i]结尾的回文子串的长度

解法区间动态规划

 

题目5:LintCode 110 Minimum Path Sum

路径上的格子数字和最小

输出最小数字和

最值型动态规划

dp[i][j] = min{dp[i-1][j], dp[i][j-1]} 

初始条件 dp[0][0]=A[0][0]

滚动数组优化空间时间复杂度 dp[0][0...n-1] 和 dp[1][0...n-1]

只依赖于上一行,来计算下一行。

 1 class Solution {
 2 public:
 3     /**
 4      * @param grid: a list of lists of integers
 5      * @return: An integer, minimizes the sum of all numbers along its path
 6      */
 7     int minPathSum(vector<vector<int>> &A) {
 8         // write your code here
 9         const int INF = 0x3f3f3f3f;
10         int m = A.size();
11         int n = A[0].size();
12         if(m==0 && n==0){
13             return 0;
14         }
15         
16         int dp[2][n];
17         int t1, t2;
18         int old=1, now=0;
19         for(int i=0; i<m; i++){  // m行
20             old = now;
21             now = 1 - now;
22             for(int j=0; j<n; j++){  // n列
23                 if(i==0 && j==0){ // 初始条件
24                     dp[now][j] = A[i][j];
25                     continue;
26                 }
27                 
28                 dp[now][j] = A[i][j];
29                 if(i>0){
30                     t1 = dp[old][j];
31                 }else{
32                     t1 = INF;
33                 }
34                 
35                 if(j>0){
36                     t2 = dp[now][j-1];
37                 }else{
38                     t2 = INF;
39                 }
40                 
41                 if(t1<t2){
42                     dp[now][j] += t1;
43                 }else{
44                     dp[now][j] += t2;
45                 }
46                 
47             }
48         }
49         
50         return dp[now][n-1];  // 计算完之后的答案始终在now一行。
51     }
52 };

 

不使用滚动数组

 1 class Solution {
 2 public:
 3     /**
 4      * @param grid: a list of lists of integers
 5      * @return: An integer, minimizes the sum of all numbers along its path
 6      */
 7     int minPathSum(vector<vector<int>> &A) {
 8         // write your code here
 9         const int INF = 0x3f3f3f3f;
10         int m = A.size();
11         int n = A[0].size();
12         if(m==0 && n==0){
13             return 0;
14         }
15         
16         int dp[m][n];
17         
18         // 初始化
19         dp[0][0] = A[0][0];
20         for(int i=1; i<m; i++){
21             dp[i][0] = dp[i-1][0]+A[i][0];
22         }
23         for(int j=1; j<n; j++){
24             dp[0][j] = dp[0][j-1]+A[0][j];
25         }
26         
27         
28         // dp计算
29         for(int i=1; i<m; i++){
30             for(int j=1; j<n; j++){
31                 dp[i][j] = min(dp[i][j-1], dp[i-1][j]) + A[i][j];
32             }
33         }
34         
35         return dp[m-1][n-1];  // 计算完之后的答案始终在now一行。
36     }
37 };

 

题目6:LintCode 553 Bomb Enemy

https://www.lintcode.com/problem/bomb-enemy/description

 

要炸死多个敌人,每个炸弹可以炸四个方向传播爆炸力。

简化分析一个方向

 

假设空地和敌人都可以放炸弹

dp[i][j] = 0                  墙

dp[i][j] = dp[i-1][j]        空地

dp[i][j] = dp[i-1][j]+1     敌人

 

(i,j)是空地  炸死的敌人数 

         dp_up[i][j] + dp_down[i][j] + dp_left[i][j] + dp_right[i][j]

 

  1 class Solution {
  2 public:
  3     /**
  4      * @param grid: Given a 2D grid, each cell is either 'W', 'E' or '0'
  5      * @return: an integer, the maximum enemies you can kill using one bomb
  6      */
  7     int maxKilledEnemies(vector<vector<char>> &A) {
  8         // write your code here
  9         
 10         int m = A.size();
 11         if(m==0){
 12             return 0;
 13         }
 14         int n = A[0].size();
 15         if(n==0){
 16             return 0;
 17         }
 18         
 19         int dp[m][n], res[m][n];
 20         
 21         for(int i=0; i<m; i++){
 22             for(int j=0; j<n; j++){
 23                 res[i][j] = 0;
 24             }
 25         }
 26         
 27         // up
 28         for(int i=0; i<m; i++){
 29             for(int j=0; j<n; j++){
 30                 if(A[i][j]=='W'){
 31                     dp[i][j] = 0;
 32                 }else{
 33                     
 34                     // 初始化值
 35                     dp[i][j] = 0;
 36                     if(A[i][j]=='E'){
 37                         dp[i][j] = 1;
 38                     }
 39                     
 40                     
 41                     // dp计算   上一行的值
 42                     if(i-1>=0){
 43                         dp[i][j] += dp[i-1][j];
 44                     }
 45                     
 46                     
 47                 }
 48                 
 49                 res[i][j] += dp[i][j];
 50             }
 51         }
 52         
 53         // down
 54         for(int i=m-1; i>=0; i--){
 55             for(int j=0; j<n; j++){
 56                 if(A[i][j]=='W'){
 57                     dp[i][j] = 0;
 58                 }else{
 59                     
 60                     // 初始化值
 61                     dp[i][j] = 0;
 62                     if(A[i][j]=='E'){
 63                         dp[i][j] = 1;
 64                     }
 65                     
 66                     
 67                     // dp计算   下一行的值
 68                     if(i+1<m){
 69                         dp[i][j] += dp[i+1][j];
 70                     }
 71                     
 72                     
 73                 }
 74                 
 75                 res[i][j] += dp[i][j];
 76             }
 77         }
 78         
 79         // lef
 80         for(int i=0; i<m; i++){
 81             for(int j=0; j<n; j++){
 82                 if(A[i][j]=='W'){
 83                     dp[i][j] = 0;
 84                 }else{
 85                     
 86                     // 初始化值
 87                     dp[i][j] = 0;
 88                     if(A[i][j]=='E'){
 89                         dp[i][j] = 1;
 90                     }
 91                     
 92                     
 93                     // dp计算   左一行的值
 94                     if(j-1>=0){
 95                         dp[i][j] += dp[i][j-1];
 96                     }
 97                     
 98                     
 99                 }
100                 
101                 res[i][j] += dp[i][j];
102             }
103         }
104         
105         // right
106         for(int i=0; i<m; i++){
107             for(int j=n-1; j>=0; j--){
108                 if(A[i][j]=='W'){
109                     dp[i][j] = 0;
110                 }else{
111                     
112                     // 初始化值
113                     dp[i][j] = 0;
114                     if(A[i][j]=='E'){
115                         dp[i][j] = 1;
116                     }
117                     
118                     
119                     // dp计算   下一行的值
120                     if(j+1<n){
121                         dp[i][j] += dp[i][j+1];
122                     }
123                     
124                     
125                 }
126                 
127                 res[i][j] += dp[i][j];
128             }
129         }
130         
131         int result = 0;
132         for(int i=0; i<m; i++){
133             for(int j=0; j<n; j++){
134                 if(A[i][j] == '0'){
135                     if(res[i][j] > result){
136                         result = res[i][j];
137                     }
138                 }
139             }
140         }
141         return result;
142         
143     }
144 };

 

序列+位操作型动态规划

位操作:

  • & 与
  • | 或
  • 异或
  • !非

 

题目7:LintCode 664 Counting Bits

 dp[i]表示i的二进制表示中有多少个1

和位操作相关的动态规划一般用值作状态

 

dp[i]表示i的二进制表示中有多少个1

dp[i] = dp[i>>1]+(i mod 2)   // 右移一位就是除以2     (i mod 2)看最后一位是0还是1

dp[0] = 0

 

时间复杂度O(NlogN)    空间复杂度O(n)

 1 class Solution {
 2 public:
 3     /**
 4      * @param num: a non negative integer number
 5      * @return: an array represent the number of 1's in their binary
 6      */
 7     vector<int> countBits(int num) {
 8         // write your code here
 9         int dp[num+1];
10         dp[0] = 0;
11         for(int i=1; i<=num; i++){
12             dp[i] = dp[i>>1] + (i%2);
13         }
14         
15         
16         vector<int> a;
17         for(int i=0; i<=num; i++){
18             a.push_back(dp[i]);
19         }
20         return a;
21     }
22 };

 

posted @ 2019-09-04 11:05  JCcodeblogs  阅读(503)  评论(0编辑  收藏  举报