背包问题与动态规划

这周主要是做动态规划的题。背包问题看的是知乎上SMON的 《动态规划之背包问题系列》。

两个背包问题模板

01背包

//
int n;// 物品数量
vector<int> w(n);//物品重量
vector<int> v(n);//物品价格
int capacity;
vector<int> dp(capacity+1,0);
for(int i = 1; i<= n; i++)
{
	for(int j = capacity; j>=w[i-1]; j--)
	{
		dp[j] = max(dp[j],dp[j-w[i] + v[i-1]);
	}
}

完全背包

int n;
vector<int> w(n);
vector<int> v(n);
int capacity;
vector<int> dp(capacity+1);
for(int i = 1; i<= n;i++)
{
	for(int j = w[i-1]; j <= capacity;j++)
	{
		dp[j] = max(dp[j], dp[j-w[i-1]]+ v[i])
	}
}

其实这个dp看起来像是状态,从考虑第1个物品,到考虑第二个物品,直到最后考虑第n个物品。

leetcode上的题目

这里都是直接抄的 SMON的答案,虽然没有自己想,但是能帮助我理解模板。

leetcode 416 分割等和子集

bool canPartition(vector<int>& nums)
{
	int sum = 0, n = nums.size();
	for(int & num:nums) sum+= num;
	if(sum%2)return false;
	int capacity = sum>>1;
	vector<bool> dp(capacity +1,false);
	dp[0] = true;
	for(int i = 1; i <= nums.size(); i++)
	{
		for(int j = capacity; j>= nums[i-1]; j--)
		{
			dp[j] = dp[j]||dp[j-nums[i-1]];
		}
	}
	return dp[capacity];
}

这里的dp其实可以理解为前i个数字是否能够正好装满 j。

leetcode 322 零钱兑换

完全背包

int coinChange(vector<int>& coins, int amount)
{
	vector<int>& dp(amount +1, INT_MAX);//此时求最小
	dp[0] = 0; //0块前最少用0个
	for(int i = 1; i <= coins.size(); i++)
	{
		for(int j = coins[i]; j<= amount; j++)
		{
			if(dp[j] > dp[j-coins[i-1]]+1)
			{
				dp[j] = 1 + dp[j - coins[i]];
			}
		}
	}
	return dp[amount]==INT_MAX?-1:dp[amount];
}

leetcode 494 目标和

这个问题将一个表达式构造问题转化成为背包问题。所有的取+的元素和为A,取 - 的元素和为B,则target = A - B, sum = A+B。则A= (target + sum)/2。我们仅仅需要找到元素和为A的就可以了。

此时的dp[j]为前i个数字满足目标j的方案数。
dp[0] : 前0个物品满足数字0的方案数:1 种 。
dp[j] = dp[j] + dp[j-nums[i-1]]
如果第i个物品不选是dp[j] ,如果选择第i个物品 ,则有dp[j-nums[i-1]]种选择。

int findTargetSumWays(vector<int>& nums, int target)
{
	int sum = 0;
	sum = accumulate(nums.begin(), nums.end(), 0);
	if(target >sum|| target < -sum) return false;
	if((sum+ target)&1)return false;
	int S= (sum + target)>>1;
	vector<int> dp(S+1, 0);
	dp[0] =1;//前0个数字组成和为0 有一种 就是 全都不取
	//注意取决策数量的时候dp[0]应该为1
	for(int i =1; i <= nums.size(); i++)
	{
		for(int j = S; j>= nums[i-1]; j--)
		{
			dp[j] = dp[j] + dp[j-nums[i-1]];
		}
	}
	return dp[S];
}

leetcode 474一和零

二维背包,更能感觉dp其实是一个随着i变化的状态矩阵。

int findMaxForm(vector<string>& str. int m, int n)
{
	int num = strs.size();
	int w0, w1;
	vector<vector<int>>dp(m+1,vector<int>(n+1, 0));
	for(int i = 1; i<= nums.size(); i++)
	{
		//计算0 的数量与1的数量
		w0 = 0,w1 = 0;
		for(char& c :nums[i-1])
		{
			if(c == '0') w0++
			else w1++;
		}
		for(int j = m; j>= w0; j--)
		{
			for(int k = n; k>= w1; k--)
			{
				dp[j][k] = max(dp[j][k], dp[j-w0][k-w1]+1);
			}
		}
	}
	return dp[m][n];
}

一个动态规划与分治集合的题目

周2做了一个leetcode312 戳气球,根据下面赞同数最高的题解思路写的。
这个题解的分治思路是:
dp[i][j] = 开区间内获得硬币的最大数量。
对于开区间 (i,j),开区间意味着i和j不会被戳破。
设i<k <j 是这个区间内最后一个戳破的气球,
则 有
d p [ i ] [ j ] = m a x ( d p [ i ] [ k ] + d p [ k ] [ j ] + n u m s [ i ] ∗ n u m s [ k ] ∗ n u m s [ j ] ) i < k < j dp[i][j] = max(dp[i][k] + dp[k][j] + nums[i]* nums[k]*nums[j]) \quad i<k<j dp[i][j]=max(dp[i][k]+dp[k][j]+nums[i]nums[k]nums[j])i<k<j

int maxCoins(vector<int>& nums)
{
	int n = nums.size();
	if(n == 1)
	{
		return nums[0];
	}
	nums.resize(n+2);
	for(int i = n-1; i>=0; i--)
	{
		nums[i+1] = nums[i];
	}
	nums[0] = 1,nums[n+1] = 1;
	
	int total = 0;
	for(int t = 2; t<= n+1; t++)
	{
		for(int i = 0; i<= n+1; i++)
		{
			total = 0;
			for(int k = i+1; k<= i+t; k++)
			{
				total = max(total, dp[i][k]*dp[k][i+t]*nums[i]*num[k]*nums[i+k] );
			}
		}
	}
	return dp[0][n+1];
}

但是开这么大的矩阵空间确实太浪费了,应该可以改进一下。

  • 17
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值