最优子结构:通过求子问题的最优解,可以获得原问题的最优解。
解法一:记忆化搜索
class Solution { private: vector<int> memo; int max3(int a, int b, int c){ return max(a, max(b,c)); } int breakInteger(int n){ //将n进行分割(至少分割两部分),可以获得的最大乘积 if(n == 1) return 1; //终止条件 if(memo[n]!= -1) return memo[n]; int res = -1; for(int i=1;i<=n-1;i++){ //i + (n-i) res = max3(res, i*(n-i), i * breakInteger(n-i)) ; memo[n] = res; } return res; } public: int integerBreak(int n) { //n为传入的数 memo = vector<int>(n+1,-1); //初始化为n+1个-1 return breakInteger(n); } };
解法二:动态规划
class Solution { private: vector<int> memo; int max3(int a, int b, int c){ return max(a, max(b,c)); } int breakInteger(int n){ //memo[i]将数字i进行分割(至少分割两部分),可以获得的最大乘积 vector<int> memo(n+1,-1); memo[1] = 1; for(int i=2;i<=n;i++){ //求解memo[i] for(int j=1; j<=i-1;j++){ // j + (i-j) memo[i] = max3(memo[i], j*(i-j), j*memo[i-j]); //j*(i-j) 表明不必继续分了 } } return memo[n]; } public: int integerBreak(int n) { //n为传入的数 memo = vector<int>(n+1,-1); //初始化为n+1个-1 return breakInteger(n); } };
思路:如下图所示,红色部分表示平方数,所有的完美平方数都可以看做一个普通数加上一个完美平方数,那么递推式就变为了:dp[i + j * j] = min(dp[i] + 1, dp[i + j * j]),dp[]存储的是最少的平方数。
class Solution { public: int numSquares(int n) { vector<int> dp(n+1, INT_MAX); for(int i=0;i*i<=n;i++){ dp[i*i] = 1; } for(int i=1; i<=n;i++){ for(int j=1;i+j*j <=n; j++){ dp[i+j*j] = min(dp[i]+1, dp[i+j*j]); } } return dp[n]; } };
输入:非空,只包含数字的字符串;
输出:能够被解码的总共方式。
class Solution { public: int numDecodings(string s) { // s[0]=='0' 代表字符串开头为0 需要return 0 if(s.empty() || (s.size()>1 && s[0]=='0') ) return 0; int n = s.size(); vector<int> dp(n+1, 0); dp[0] = 1; for(int i=1;i<dp.size();i++){ //s数组的下标从0开始,相应的dp从1开始 dp[i] = (s[i-1] == '0') ? 0 : dp[i-1]; //只考虑一位的情况 if(i>1 && (s[i-2]=='1' || (s[i-2]=='2' && s[i-1]<='6') ) ) //当十位数为1时,个位数可以任意取值;而十位数为2时,个位数只能取从0-6的数 dp[i] += dp[i-2]; // dp[i] = dp[i-1] + dp[i-2] } return dp.back(); } };
62
递推式:dp[i][j] = dp[i-1][j] + dp[i][j-1];
class Solution { public: int uniquePaths(int m, int n) { int dp[m][n]; dp[0][0] = 1; for(int i=1;i<m;i++) dp[i][0] = 1; for(int i=1;i<n;i++) dp[0][i] = 1; for(int i=1;i<m;i++){ for(int j=1;j<n;j++) dp[i][j] = dp[i-1][j] + dp[i][j-1]; } return dp[m-1][n-1]; } };
63
思路:当遇到为1的点时,将该位置的dp数组的值清零。
class Solution { public: int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) { if(obstacleGrid.empty() || obstacleGrid[0].empty() || obstacleGrid[0][0]==1) return 0; int m = obstacleGrid.size(); int n = obstacleGrid[0].size(); 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) dp[i][j] = 1; else if(i==0 && j>0) dp[i][j] = dp[i][j-1]; else if(i>0 && j==0) dp[i][j] = dp[i-1][j]; else dp[i][j] = dp[i-1][j] + dp[i][j-1]; } } return dp[m-1][n-1]; } };
解法一:自顶向下:
class Solution { private: //memo[i]表示考虑去抢劫nums[i...n)所能获得的最大收益 vector<int> memo; //考虑抢劫 nums[index...nums.size()] 这个范围的所有房子 int tryRob(vector<int> &nums, int index){ if(index >= nums.size()) //空集 return 0; if(memo[index] != -1) return memo[index]; int res = 0; for(int i=index; i<nums.size();i++){ res = max(res, nums[i] + tryRob(nums, i+2)); } memo[index] = res; return res; } public: int rob(vector<int>& nums) { memo = vector<int>(nums.size(), -1); return tryRob(nums, 0); } };
解法二:动态规划