零钱兑换
完全背包问题;
class Solution { public: int coinChange(vector<int>& coins, int amount) { int n = coins.size(); vector<int> dp(amount + 1, amount + 1); dp[0] = 0; for(int i = 1; i <= amount; i++){ for(int j = 0; j < n; j++){ if(coins[j] <= i){ dp[i] = min(dp[i], dp[i - coins[j]] + 1); } } } return dp[amount] <= amount ? dp[amount] : -1; } };
打家劫舍 3
树上 DP,DFS,偷为 a 不偷为 b,注意父节点不偷子节点偷不偷均可,父节点偷子节点只能不偷;
class Solution { public: struct dpnode{ int a, b; dpnode(int x, int y) : a(x), b(y) {} }; dpnode dfs(TreeNode* root){ if(!root){ return dpnode(0, 0); } dpnode ans = dpnode(0, 0); dpnode t1 = dfs(root -> left); dpnode t2 = dfs(root -> right); ans.a = t1.b + t2.b + root -> val; ans.b = max(t1.a, t1.b) + max(t2.a, t2.b); return ans; } int rob(TreeNode* root) { dpnode ans = dfs(root); return max(ans.a, ans.b); } };
分割等和子集
背包问题
class Solution { public: bool canPartition(vector<int>& nums) { int amount = 0, n = nums.size(), maxn = 0; for(int i = 0; i < n; i++){ amount += nums[i]; maxn = max(maxn, nums[i]); } if(amount & 1 || maxn * 2 > amount){ return false; }else{ amount = amount >> 1; vector<int> dp(amount + 1, 0); dp[0] = true; for(int i = 0; i < n; i++){ int num = nums[i]; for(int j = amount; j >= num; j--){ dp[j] |= dp[j - num]; } } return dp[amount]; } } };
目标和
笨方法 DP;
class Solution { public: int findTargetSumWays(vector<int>& nums, int target) { int amount = 0, n = nums.size(); for(int i = 0; i < n; i++){ amount += nums[i]; } if(amount < abs(target)){ return 0; } int dp[n][amount + 1]; memset(dp, 0, sizeof(dp)); if(nums[0] == 0){ dp[0][nums[0]] = 2; }else{ dp[0][nums[0]] = 1; } for(int i = 1; i < n; i++){ for(int j = 0; j <= amount; j++){ if(nums[i] == 0){ dp[i][j] = dp[i - 1][j] * 2; }else{ if(j >= nums[i]){ dp[i][j] += dp[i - 1][j - nums[i]]; }else{ dp[i][j] += dp[i - 1][abs(j - nums[i])]; } if(j + nums[i] <= amount){ dp[i][j] += dp[i - 1][j + nums[i]]; } } } } return dp[n - 1][abs(target)]; } };
优化分两个集合一个是正组成的,一个是负组成的,所以正之和应为(sum + target)/2;
class Solution { public: int findTargetSumWays(vector<int>& nums, int target) { int amount = 0, n = nums.size(); for(int i = 0; i < n; i++){ amount += nums[i]; } if(amount < abs(target) || (amount + target) & 1 == 1){ return 0; } amount = (target + amount) >> 1; int dp[amount + 1]; memset(dp, 0, sizeof(dp)); dp[0] = 1; for(int num : nums){ for(int j = amount; j >= num; j--){ dp[j] += dp[j - num]; } } return dp[amount]; } };
回文子串
逻辑和最长回文子串一样,中心扩散;
class Solution { public: int countSubstrings(string s) { int n = s.size(),ans = 0; int dp[n][n]; memset(dp,0,sizeof(dp)); for(int i = 0;i < n;i++){ dp[i][i] = 1; ans++; } for(int i = n - 1;i >= 0;i--){ for(int j = i + 1;j < n;j++){ if(s[i] == s[j] && (j - i == 1 || dp[i + 1][j - 1] == 1)){ dp[i][j] = 1; dp[j][i] = 1; ans++ ; } } } return ans; } };