本周题目:
目标和 —— 01背包问题
问题的难点在于问题的转化。
思路就是把整个集合看成两个子集,Q表示整个集合,P表示正数子集,N表示负数子集, T表示目标和,用S(X)S(X)表示集合的求和函数,集合中均为非负数,N集合是指选中这部分元素作为负数子集。
S ( P ) − S ( N ) = T S(P) - S(N) = T S(P)−S(N)=T
S ( P ) − S ( N ) = T S(P)−S(N)=T S(P)−S(N)=T
S ( P ) + S ( N ) + S ( P ) − S ( N ) = T + S ( P ) + S ( N ) S(P) + S(N) + S(P) - S(N) = T + S(P) + S(N) S(P)+S(N)+S(P)−S(N)=T+S(P)+S(N)
S ( P ) + S ( N ) + S ( P ) − S ( N ) = T + S ( P ) + S ( N ) S(P)+S(N)+S(P)−S(N)=T+S(P)+S(N) S(P)+S(N)+S(P)−S(N)=T+S(P)+S(N)
2 S ( P ) = S ( Q ) + T 2S(P) = S(Q) + T 2S(P)=S(Q)+T
2 S ( P ) = S ( Q ) + T 2S(P)=S(Q)+T 2S(P)=S(Q)+T
也就是:正数集的和的两倍 == 等于目标和 + 序列总和。接下来就是标准的01背包问题了。
作者:bao-bao-ke-guai-liao
链接:https://leetcode-cn.com/problems/target-sum/solution/c-dfshe-01bei-bao-by-bao-bao-ke-guai-liao/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
零钱兑换
有一种感想动态规划本质是不会受到循环嵌套的内外层循序影响的,原因在于,不论动态规划的过程是自上而下/自下而上还是自左上到右下等等,都可以通过先行后列扫描或者先列后行扫描,这个问题太明显的,我居然还在纠结于此。
此题不普通的背包问题不同在于==一般的背包问题dp[weight]的含义是小于等于weight的目标值是多少,而此题的不同就是等于weight的目标值是多少。==其实现的方案就是通过初始化是制造边界条件。使得求解dp[weigth]时排除小于weight的目标值。
具体代码如下:
int coinChange(vector<int>& coins, int amount) {
int length = coins.size();
if(length == 0)
return -1;
vector<int> dp(amount+1, amount + 1);
dp[0] = 0;
sort(coins.begin(), coins.end());
for(int coin = coins[0]; coin <= amount; ++coin){
for(int i = 0; i < length; ++i){
if(coins[i] <= coin){
dp[coin] = min(dp[coin], 1 + dp[coin - coins[i]]);
}
}
}
return dp[amount] > amount? -1 : dp[amount];
}
零钱兑换2
典型的完全背包问题。
int change(int amount, vector<int>& coins) {
int length = coins.size();
if(length == 0){
if(amount == 0)
return 1;
if(amount > 0)
return 0;
}
vector<int> dp(amount+1,0);
sort(coins.begin(), coins.end());
dp[0] = 1;
//dp[coins[0]] = 1;
for(int i = 0; i < length; ++i){
for(int weight = coins[i]; weight < amount+1; ++weight){
dp[weight] += dp[weight-coins[i]];
}
}
return dp[amount];
}
一和零
标准的二维背包问题。核心代码如下:
int findMaxForm(vector<string>& strs, int m, int n) {
int length = strs.size();
if(length == 0)
return 0;
vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
for(int k = 0; k < length; ++k){
int num_1 = 0, num_0 = 0;
for(int x = 0; x < strs[k].size(); ++x){
if(strs[k][x] == '0')
++num_0;
else
++num_1;
}
for(int i = m; i >= num_0; --i){
for(int j = n; j >= num_1; --j){
dp[i][j] = max(dp[i][j], 1+ dp[i-num_0][j-num_1]);
}
}
}
return dp[m][n];
}
二叉树中的最大路径和
使用回溯法计算,递归计算每一个节点的左右分支的最路径,然后求出以当前节点为根节点的最大路径和,直至求出根节点的最大路径和。
class Solution {
private:
int maxSum;
int getMaxPath(TreeNode* node)
{
if(node == NULL)
return 0;
int leftNode = max(getMaxPath(node->left),0);
int rightNode = max(getMaxPath(node->right),0);
maxSum = max(maxSum, node->val + leftNode + rightNode);
return node->val + max(leftNode,rightNode);
}
public:
int maxPathSum(TreeNode* root) {
maxSum = INT_MIN;
getMaxPath(root);
return maxSum;
}
};