【0-1背包、分数背包、最优装载、装载问题】动态规划、贪心、回溯

A.0-1背包问题:
n件物品重W[1:n],价值V[1:n],背包容量为C
1.动态规划:

  •    动态规划即找准dp的表达式,分析0-1背包问题是需要找到背包最多能装多大价值的物品,那么此处我们的dp[i][j],就如此来定义:
    

dp[i][j]代表前i件物品,装入容量为j的背包的最大价值,比如dp[0][j] = 0, dp[i][0] = 0,dp[n][C]就是我们最后要求的装入前n件物品于容量为C的背包的最大价值。那么dp应该怎样往后一步一步算呢?

dp[i][j]是指前i件物品装入容量为j的背包的最大价值,那么就有两种选择项:
a.将第i件物品装进去,那么就必须得要装入i之前的这个容量j还能装得下第i件物品,即j > W[i],此时的dp[i][j] = dp[i - 1][j - W[i]] + V[i],也就是说,要把i装进去,那么i- 1的容量就只能用j - W[i]的容量来参与计算;
b.第i件物品就不放进去了,那dp[i][j] = dp[i-1][j];
比较以上两者取最大值,代码如下:

int dp[n + 1][C + 1];
for(i = 0; i <= n; i++)
	dp[i][0] = 0;
for(j = 0; j <= C; j++)
	dp[0][j] = 0;
for(i = 1; i <= n; i++)
{
	for(j = 1; j <= C; j++)
	{
		dp[i][j] = dp[i - 1][j];
		if(j > W[i])
		{
			int pt = dp[i - 1][j - W[i]] + V[i];
			if(pt > dp[i][j])
				dp[i][j] = pt;
		}
	}
}

还有一种更简便的算法,这里只给出代码:

int g[C];
memeset(g,0,sizeof(g));
for(i = 1; i <= n; i++)
{
	for(j = C; j >= 1; j--)
	{
		if(j > W[i])
		{
			pt = g[j - W[i]] + V[i];
			if(pt > g[j])
				g[i] = pt;
		}
	}
}

2.回溯:

  • 回溯就是选还是不选的问题, 我们要考虑到每一种组合,考虑每一个物品的选择和不选择的问题, 在遍历的过程中,我们寻求合适的剪枝(约束条件和限界条件)
    

先看遍历代码:

void traceback(int t)
{
	if(t > n)
	{
		if(sum > ans)
			ans = sum;
		return;
	}
	if(pt + W[t] <= C)
	{
		pt += W[t];
		sum += V[t];
		traceback(t + 1);
		pt -= W[t];
		sum -= V[t];
	}
	traceback(t + 1);
	return;
}

上述代码我们的约束条件有了, 如果想进一步减少计算复杂度的话,我们可以再考虑加一个限界条件:
用r[i]记录到第i行的时候,一共有多少价值,即:

r[0] = 0;
r[1] = V[1];
r[i] = r[i - 1] + V[i];

新的回溯代码:

void traceback(int t)
{
	if(t > n)
	{
		if(sum > ans)
			ans = sum;
		return;
	}
	if(sum + r[N] - r[t - 1] < ans)
		return;
	if(pt + W[t] <= C)
	{
		sum += V[t];
		pt += W[t];
		traceback(t + 1);//x[t] = 1;
		sum -= V[t];
		pt -= W[t];
	}
	traceback(t + 1);//x[t] = 0;
	return;
}

B.分数背包问题:
n件物品重W[1:n],价值为V[1:n],背包容量为C,与0-1背包问题不同的是,分数背包问题取物品的时候可以取0 ~ W[i]重的物品,当然不取完,得到的价值也不会是全部的,也是根据取得量来定的。
贪心算法:
由于每件物品不需要全部取走,可以取一部分,那么我们就可以来计算没意见物品的“性价比”,看取多少重量的某件物品能够获得最大的价值,因此我们就再计算一个数组X[i] = V[i] / W[i],对数组X进行由大到小的排序,从大的往小的取,大的全部装进去,直到最后装不下了为止。

C.最优装载:
n件物品重W[1:n],背包容量为C,怎么取物品能使得取得件数最多。

贪心算法:
对每件物品按照重量由小到大排序,按重量与小达到往背包里面装物品,直到背包里面装不下为止。

D.装载问题:
n件物品重W[1:n],背包容量为C,怎么取能使得装载重量最大。

回溯算法:
每件物品有两个选择,装进去或者不装进去,在我们的回溯遍历中,就需要体现出这里有两种选择:

void traceback(int t)
{
	if(t > n)
	{
		if(sum > ans)
			ans = sum;
		return;
	}
	if(sum + W[t] <= C)
	{
		sum += W[t];
		traceback(t + 1);//x[t] = 1;
		sum -= W[t];
	}
	traceback(t + 1);//x[t] = 0;
	return;
}

如同0-1背包问题一样,这里只是有不同的选择条件,为了简化算法,我们应该添加限界条件:

r[0] = 0;
r[1] = W[i];
r[i] = r[i - 1] + W[i];
void traceback(int t)
{
	if(t > n)
	{
		if(sum > ans)
			ans = sum;
		return;
	}
	if(sum + r[N] - r[t - 1] < ans)
		return;
	if(sum + W[i] <= C)
	{
		sum += W[i];
		traceback(t + 1);//x = 1;
		sum -= W[i];
	}
	traceback(t + 1);//x = 0;
	return ;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
0-1背包问题是一个经典的动态规划问题。其具体描述如下:给定一个固定大小、能够携带重量为W的背包,以及一组有价值和重量的物品。找出一个最佳的方案,使得装入背包的物品总重量不超过W,且总价值最大。 动态规划法和回溯法都可以用来解决0-1背包问题,但它们的思路和实现方式有所不同。 1. 动态规划动态规划法是将问题分解成若干个子问题,并分别求解这些子问题的最优解,从而得到原问题的最优解。在0-1背包问题中,动态规划法的思路是:用一个二维数组dp[i][j]表示前i个物品放入容量为j的背包中所能获得的最大价值。则动态转移方程为: dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]),其中w[i]和v[i]分别表示第i个物品的重量和价值。 2. 回溯回溯法是一种通过搜索所有可能的解来解决问题的方法。在0-1背包问题中,回溯法的思路是:对于每个物品,都有两种选择:放入背包或不放入背包。因此,可以通过回溯的方式搜索所有可能的解。具体的实现方式是用一个递归函数backtrack来搜索所有可能的解,并在搜索过程中更新最大价值。 回溯法的缺点是会搜索很多无用的状态,因此时间复杂度较高。而动态规划法则可以通过空间换时间的方式,避免对重复状态的搜索,从而提高效率。因此,在解决0-1背包问题时,动态规划法更为常用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值