题目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-1个n-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 };
牛客笔试题目:解码方式
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 };
牛客题目:回文字符串
设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 };