0
点赞
收藏
分享

微信扫一扫

无聊的逗(0-1背包问题)

骨灰级搬砖工 2022-02-28 阅读 59

在这里插入图片描述

bool canPartition(vector<int>& nums)
{
	int n = nums.size();
	
	if(n < 2)	return false;
	
	int sum = accumulate(nums.begin(), nums.end(), 0);
	
	int maxNum = *max_element(nums.begin(), nums.end());
	
	if(sum & 1)	return false;
	
	int target = sum / 2;
	
	if(maxNum > target) return false;
	
	// 建立背包
	
	vector<vector<int>> dp(n, vector<int>(target + 1, 0));
	
	// 初始化
	for(int i = 0; i < n; ++ i)	dp[i][0] = 0;
	for(int j = nums[0]; j <= target; ++ j) dp[0][j] = nums[0];
	
	for(int i = 1; i < n; ++ i)
	{
		for(int j = 1; j <= target; ++ j)
		{
			if(j < nums[i])	//如果新增的一个物品质量超过背包容量 
			{
				dp[i][j] = dp[i - 1][j];	//物品不放进背包,能装进背包的最大质量是新增物品之前的质量 
			}else{	// 如果没有超过 
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]);
			}
		}
	}
	return dp[n - 1][target] == sum / 2;
}
// dp[i][j] = 有i个物品,分成两个相等的子集,最大重量是多少
// dp[i][0] = 0  有i个物品,分成两个相等中、重量为0的子集,最大质量就是0
// dp[0][j] = nums[0]  没有什么实际意义但是在求dp[i][j]的时候会用到。

在这里插入图片描述
样例输入

4
1 2 3 1

样例输出

3

数据规模

n<=15
#include<bits/stdc++.h>
using namespace std;

//判断是否能分割成两个相等子序列,存在则返回sum/2 ,否则返回 0 
int getPartitionValue(vector<int> nums)
{
	int n = nums.size();
	if(n < 2) return 0;		// 如果只有一个木棍 返回 0 
	int sum = accumulate(nums.begin(), nums.end(), 0);		// 计算所有木棍的总长 
	int maxNum = *max_element(nums.begin(), nums.end());	// 找到最长木棍的第一个位置 
	int target = sum / 2;
	if(maxNum > target)	return 0;
	//建立背包
	vector<vector<int> > dp(n, vector<int>(target + 1, 0));		
	// 相当于一个二维int数组,dp[n][target + 1] ,并且初始化所有值为 0 
	
	//初始化背包
	for(int i = 0; i < n; ++ i)	dp[i][0] = 0;
	for(int j = nums[0]; j <= sum / 2; ++ j)	dp[0][j] = nums[0];
	for(int i = 1; i < n; ++ i)
	{
		for(int j = 1; j <= target; ++ j)
		{
			if(j < nums[i])
			{
				dp[i][j] = dp[i - 1][j];
			} else{
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]);
			}
		}
	}
	return dp[n - 1][target] == sum / 2 ? sum /2 : 0;
}
int main()
{
	int n;
	cin >> n;	//n个棍子 
	vector<int> nums(n);
	for(int i = 0; i < n; ++ i)
		cin >> nums[i];	//棍子长度 
		
	sort(nums.begin(), nums.end());	//对棍子长度排序
	int sum = accumulate(nums.begin(), nums.end(), 0);	//计算棍子总长
	
	int i = 0;
	int ans = 0;
	
	//在不删除棍子情况下,sum如果为偶数,判断是否能分割成两个相等子序列,并保存结果
	if(sum % 2 == 0)	ans = getPartitionValue(nums);
	
	//如果每次总和是偶数,都去判断是否存在可分成使得两个子集的元素和相等,如果存在返回值 
	while(i < nums.size()) 
	{
		sum = accumulate(nums.begin(), nums.end(), 0);
		if((sum - nums[i]) % 2 == 0)
		{
			nums.erase(nums.begin() + i);
			ans = max(getPartitionValue(nums), ans);
			i = 0;
			continue;
		}
		++ i;
	}
	cout << ans << endl;
	return 0;
} 

参考文章

举报

相关推荐

0 条评论