算法学习——动态规划之背包问题

目录

一、基础01背包

1、二维 dp 数组

2、滚动数组优化为一维数组

二、01背包应用

1、求装满后的最大价值变形

2、求能不能装满

3、求装满有几种办法

4、求装满后背包有多大(二维背包)

三、基础完全背包

四、完全背包应用

1、求装满有几种办法(组合数)

2、求装满有几种办法(排列数)

3、求装满时最少用多少物品

4、求能不能装满背包(排列数)


一、基础01背包

        即各物品数量只有一件,问装满价值最大是多少

1、二维 dp 数组

        定义 dp[i][j] 为从下标 [0...i] 的物品中任选,放进容量为 j 的背包里,能获得的最大价值

int knapsack_01_1(vector<int>& weight, vector<int>& value, int capacity)
{
	int n = weight.size();
	vector<vector<int>> dp(n, vector<int>(capacity + 1));
	for (int j = weight[0]; j <= capacity; j++)
	{
		dp[0][j] = value[0];
	}
	for (int i = 0; i < n; i++)
	{
		for (int j = weight[i]; j <= capacity; j++)
		{
			dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
		}
	}
	return dp[n - 1][capacity];
}

2、滚动数组优化为一维数组

         定义 dp[j] 为容量为 j 的背包能获得的最大价值

int knapsack_01_2(vector<int>& weight, vector<int>& value, int capacity)
{
	int n = weight.size();
	vector<int> dp(capacity + 1);
	for (int i = 0; i < n; i++)
	{
		for (int j = capacity; j >= weight[i]; j--)
		{
			dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
		}
	}
	return dp[capacity];
}

二、01背包应用

1、求装满后的最大价值变形

        原题力扣1049        最后一块石头的重量II        . - 力扣(LeetCode)

int lastStoneWeightII(vector<int>& stones)
{
	int n = stones.size();
	int sum = 0;
	for (const auto& x : stones)
	{
		sum += x;
	}
	int target = sum / 2;
	vector<int> dp(target + 1, 0);
	for (int j = stones[0]; j <= target; j++)
	{
		dp[j] = stones[0];
	}
	for (int i = 1; i < n; i++)
	{
		for (int j = target; j >= stones[i]; j--)
		{
			dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
		}
	}
	int rest = sum - dp[target];
	return rest - dp[target];
}

2、求能不能装满

        原题力扣416        分割等和子集        . - 力扣(LeetCode)

bool canPartition(vector<int>& nums)
{
	int n = nums.size();
	int sum = 0;
	for (const auto& x : nums)
	{
		sum += x;
	}
	if (sum % 2 != 0)
	{
		return false;
	}
	int target = sum / 2;
	vector<int> dp(target + 1, 0);
	for (int i = 0; i < nums.size(); i++)
	{
		for (int j = target; j >= nums[i]; j--)
		{
			dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
		}
	}
	return dp[target] == target;
}

3、求装满有几种办法

        力扣原题494        目标和        . - 力扣(LeetCode)

int findTargetSumWays(vector<int>& nums, int target)
{
	int n = nums.size();
	int sum = 0;
	for (const auto& x : nums)
	{
		sum += x;
	}
	int positive = (sum + target) / 2;
	if ((sum + target) % 2 != 0)
	{
		return 0;
	}
	if (abs(target) > sum)
	{
		return 0;
	}
	vector<int> dp(positive + 1);
	dp[0] = 1;
	for (int i = 0; i < n; i++)
	{
		for (int j = positive; j >= nums[i]; j--)
		{
			dp[j] += dp[j - nums[i]];
		}
	}
	return dp[positive];
}

4、求装满后背包有多大(二维背包)

        力扣原题474        一和零        . - 力扣(LeetCode)

int findMaxForm(vector<string>& strs, int m, int n)
{
	vector<vector<int>> dp(m + 1, vector<int>(n + 1));
	dp[0][0] = 0;
	for (const auto& str : strs)
	{
		int zero = 0, one = 0;
		for (const auto& ch : str)
		{
			if (ch == '0')
			{
				zero++;
			}
			else
			{
				one++;
			}
		}
		for (int i = m; i >= zero; i--)
		{
			for (int j = n; j >= one; j--)
			{
				dp[i][j] = max(dp[i][j], dp[i - zero][j - one] + 1);
			}
		}
	}
	return dp[m][n];
}

三、基础完全背包

        即各物品数量不限,问装满价值最大是多少

int knapsack_complete(vector<int>& weight, vector<int>& value, int capacity)
{
	int n = weight.size();
	vector<int> dp(capacity + 1, 0);
	for (int i = 0; i < weight.size(); i++)
	{
		for (int j = weight[i]; j <= capacity; j++)
		{
			dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
		}
	}
	return dp[capacity];
}

四、完全背包应用

1、求装满有几种办法(组合数)

        原题力扣518        零钱兑换II        . - 力扣(LeetCode)

int change(int amount, vector<int>& coins)
{
	int n = coins.size();
	vector<int> dp(amount + 1);
	dp[0] = 1;
	for (int i = 0; i < n; i++) // 遍历物品
	{
		for (int j = coins[i]; j <= amount; j++) // 遍历背包
		{
			dp[j] += dp[j - coins[i]];
		}
	}
	return dp[amount];
}

2、求装满有几种办法(排列数)

        原题力扣377        组合总和IV        . - 力扣(LeetCode)

int combinationSum4(vector<int>& nums, int target)
{
	vector<int> dp(target + 1);
	dp[0] = 1;
	for (int i = 0; i <= target; i++) // 遍历背包
	{
		for (int j = 0; j < nums.size(); j++) // 遍历物品
		{
			if (i >= nums[j])
			{
				dp[i] += dp[i - nums[j]];
			}
		}
	}
	return dp[target];
}

3、求装满时最少用多少物品

        原题力扣279        完全平方数        . - 力扣(LeetCode)

int numSquares(int n)
{
	vector<int> dp(n + 1, INT_MAX);
	dp[0] = 0;
	for (int i = 1; i <= n; i++)
	{
		for (int j = i * i; j <= n; j++)
		{
			dp[j] = min(dp[j], dp[j - i * i] + 1);
		}
	}
	return dp[n];
}

        原题力扣322        零钱兑换        . - 力扣(LeetCode)

int coinChange(vector<int>& coins, int amount)
{
	vector<int> dp(amount + 1, INT_MAX);
	dp[0] = 0;
	for (int i = 0; i < coins.size(); i++)
	{
		for (int j = coins[i]; j <= amount; j++)
		{
			if (dp[j - coins[i]] != INT_MAX)
			{
				dp[j] = min(dp[j], dp[j - coins[i]] + 1);
			}
		}
	}
	if (dp[amount] == INT_MAX)
	{
		return -1;
	}
	return dp[amount];
}

4、求能不能装满背包(排列数)

        原题力扣139        单词拆分        . - 力扣(LeetCode)

bool wordBreak(string s, vector<string>& wordDict)
{
	int n = s.size();
	unordered_set<string> st(wordDict.begin(), wordDict.end());
	vector<bool> dp(n + 1);
	dp[0] = true;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j < i; j++)
		{
			if (st.find(s.substr(j, i - j)) != st.end() &&
				dp[j] == true)
			{
				dp[i] = true;
			}
		}
	}
	return dp[n];
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值