01背包,完全背包,多重背包

-查阅了很多相关的博客,总结了一下,和我之前总结二分搜索的情况一样,3种背包都统一一下模板,为了方便记忆,只需要掌握第二层循环都用倒序进行操作的就行了,为什么倒序是参考文末的第二篇CSDN博客,每一个i值下面的循环,从j开始更新不会覆盖j-1到0的f值,因为用的是一维数组,所以要考虑到覆盖的问题。如果从低到高更新假如一共有10个物品,在第i个循环中,假如已经更新到第5个,也就是说前4个都是更新过的,而且是在第i个循环中更新的,那么当第6-10个更新的时候,如果用到0-4的值,那么此时0-4的值是和6-10在同一层循环(第i层)中的数,而不是上一层循环的数,是被“破坏之后的数”。!!!!!每个i都应该相比于上一层循环(i-1层)多操作一次,操作为:是放到背包里还是不放到背包里。本来就是一次尝试,所以第i层的尝试要根据i-1的那层循环的f的值进行更新,而不能使用本层,也就是i层的f值进行更新。如果第i层用了第i层更新的值,01背包问题就转化成了完全背包问题,所以完全背包问题有一种写法和01背包一样就是第二层循环正序开始更新,所以:
完全背包问题有两种写法,第一种是:二层循环用倒序加上for循环,第二种写法是正序,其余更新代码和01背包一样;
01背包有两种写法:一种是二维数组,加上正序,第二种是二层循环用倒序写;
多重背包其实和完全背包差不多,只是加了个判断条件,每件物品的数目是一定的。

初始化
“恰好装满背包”时的最优解与没有要求必须把背包装满。
要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1…V]均设为-∞,这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。
如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0…V]全部设为0。
可以这样理解:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。
如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。
这个小技巧完全可以推广到其它类型的背包问题。

下面来看代码:

#include
#include
#include
#include
using namespace std;
// 01背包解法1
// 01背包二维数组的是顺序的代码    f[i][j] 代表前i个物品   背包装了重量为j的物品的最大利润
int Bag_01(int f[7][13],int cost[],int V[],int bagV,int size)
{
	for (int i = 1; i <= size; ++i)
	{
		for (int j = 0; j <= bagV; j++)
		{
			if (j < V[i-1])
				f[i][j] = f[i - 1][j];
			else
				f[i][j] = max(f[i - 1][j], f[i - 1][j - V[i-1]] + cost[i-1]);
		}
	}
	return f[5][bagV];
}
// 01背包解法2
// 01背包问题改进
int Bag_better(int f[], int cost[], int V[], int bagV, int size)
{
	//逆序代码
	for (int i = 1; i<= size; ++i)
	{
		for (int j = bagV; j >= V[i - 1]; j--)
		{
			f[j] = max(f[j - V[i - 1]] + cost[i - 1], f[j]);
		}
	}
	return f[bagV];
}
// 完全背包解法1
// 完全背包问题就是加了一个循环
//完全背包在选第i种物品时,容积够用情况下,可能有2种以上状态可选,放1个,或者2个,3个,无限,或者不放。找出最大价值的选择
int Bag_wanquan(int f[], int cost[], int V[], int bagV, int size)
{
	for (int i = 1; i <= size; ++i)
	{
		for (int j = bagV; j >= V[i - 1]; j--)
		{
			int k = j / V[i - 1];
			for (int m = 0; m <= k; ++m)
			{
				f[j] = max(f[j - m*V[i - 1]] + m*cost[i-1], f[j]);
			}
		}
	}
	return f[bagV];
}
// 完全背包解法2
int Bag_wanquan2(int f[], int cost[], int V[], int bagV, int size)
{
	//顺序代码
	for (int i = 1; i <= size; ++i)
	{
		for (int j = V[i - 1]; j<= bagV; j++)
		{
			f[j] = max(f[j - V[i - 1]] + cost[i - 1], f[j]);
		}
	}
	return f[bagV];
}
//多重背包问题   放的数量有限制num[i]
int Bag_multi(int f[], int cost[], int V[], int bagV, int num[], int size)
{
	for (int i = 1; i <= size; ++i)
	{
		for (int j = bagV; j >= V[i - 1]; j--)
		{
			for (int m = 0; m <= num[i-1]; ++m)
			{
				if(j>=m* V[i - 1])
					f[j] = max(f[j - m* V[i-1]] + m * cost[i-1], f[j]);
			}
		}
	}
	return f[bagV];
}
int main() {
	int cost[5] = { 2,5,3,10,4 };               // 每个物品的花费
	int V[5] = { 1,3,2,6,2 };					// 相对应的物品所占用的体积
	int num[6] = { 1,2,3,4,5 };   				// 多重背包问题,每个物品的数量
	int size = sizeof(cost)/sizeof(int);        // 物品的数量
	int f[7][13] = { 0 };                       // 01背包二维数组的初始化
	int f2[13] = { 0 };							// 01背包方法2中用到的一维数组的初始化
	int f3[13] = { 0 };							// 完全背包和多重背包中用到的一维数组的初始化
	int bagV = 12;							    // 背包的容量
	int bag_01 = Bag_01(f, cost, V, bagV ,size);
	cout << "01背包问题: " << bag_01<<endl;
	int bag_02 = Bag_better(f2, cost, V, bagV ,size);
	cout << "01背包优化问题: " << bag_02 << endl;
	int bag_wanquan = Bag_wanquan(f3, cost, V, bagV, size);
	cout << "完全背包问题: " << bag_wanquan << endl;
	int bag_wanquan2 = Bag_wanquan2(f3, cost, V, bagV, size);
	cout << "完全背包问题2: " << bag_wanquan << endl;
	int bag_multi = Bag_multi(f3, cost, V, bagV,num, size);
	cout << "多重背包问题: " << bag_multi << endl;
	return 0;
}

参考博客: https://blog.csdn.net/WSTONECH/article/details/88746104
https://www.cnblogs.com/fengziwei/p/7750849.html

你可能感兴趣的:(c++,数据结构,动态规划)