完全背包问题以及优化小技巧

完全背包问题

题目:给定一个数组Arr,每一个元素表示一种硬币 ,数量无限。现在需要这些种硬币组成我们的目标是aim,问有多少中组合的方法;

Arr = {3,2,1,5,7,3},aim = 20;

    1. 递归解法
      寻找可变参数:idx表示从数组中的第idx以后的元素中找到组成rest的组合数;rest的初始值为aim;这类递归问题可以想成是一颗树;
      f(idx,rest)表示数组Arr的第idx以后的元素集合,从这个集合中选取硬币组成rest的组合数;
  • code

//表示从数组种的idx位置开始以后种类的货币组成rest有多少种方法
int get_N1(vector<int>& Arr, int idx, int rest)
{
	if (idx == Arr.size())
		return rest == 0 ? 1 : 0;
	int res = 0;
	for (int k = 0; k * Arr[idx] <= rest; k++)
	{
		res += get_N1(Arr, idx + 1, rest - Arr[idx] * k);
	}
	return res;
}

for循环的意义就是一个节点从所有的孩子拿信息,只不过这里的孩子说是根据给定的Arr已经aim才能确定,所以写成这种形式;

举例说明:Arr=[3,4,2,34,7,23],aim=20
面值3的硬币使用0次,get_N1(Arr,1,aim)
面值3的硬币使用1次,get_N1(Arr,1,aim-31)
面值3的硬币使用2次,get_N1(Arr,1,aim-3
2)
面值3的硬币使用6次,get_N1(Arr,1,aim-3*6)
使用7次,rest就小于零了;并且一直使用面值3,不能达到aim;

    1. 严格表结构
/严格表结构
int get_N2(vector<int>& Arr, int idx, int rest)
{
	vector<vector<int>> dp(Arr.size() + 1, vector<int>(rest+1, 0));
	dp[Arr.size()][0] = 1;
	for (int i = Arr.size() - 1; i >= 0;i--)
	{
		for (int j = 0; j <= rest; j++)
		{
			/*int k = j;
			while (k >= 0)
			{
				dp[i][j] = dp[i+1][k];
				k -= Arr[i+1];
			}*/
			int res = 0;
			for (int k = 0; k * Arr[idx] <= rest; k++)
			{
				res += dp[i + 1][rest - Arr[i] * k];
			}
			dp[i][j] += res;
		}
	}
	return dp[idx][rest];
}
    1. 优化版本
      复用dp数组的信息
/严格表结构
int get_N2(vector<int>& Arr, int idx, int rest)
{
	vector<vector<int>> dp(Arr.size() + 1, vector<int>(rest+1, 0));
	dp[Arr.size()][0] = 1;
	for (int i = Arr.size() - 1; i >= 0;i--)
	{
		for (int j = 0; j <= rest; j++)
		{
			dp[i][j] = dp[i + 1][j];
			if (j - Arr[i] >= 0)
			{
				if (j - Arr[i] >= 0)
					dp[i][j] += dp[i][j - Arr[i]];
			}
		}
	}
	return dp[idx][rest];
}
  1. 心得
    动态规划的题目重要是找到尝试的思路,也就是递归版本;可以i尝试左右边界,取值范围等;找到可变参数是解题的关键,可变参数越少越好,可变参数最好是一个值,不要使用数组类型或者其他类型的可变参数,最后确定可变参数的范围
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星光技术人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值