背包问题-01背包,完全背包,多重背包

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SZU_Crayon/article/details/82432909
  • 背包问题-01背包,完全背包,多重背包


  • 01背包:

概念:

有Goods_Num件物品,MAX_Volume的最大装载量,每种物品只有一件,每种物品都有对应的重量或者说体积Volume[i],价值Value[i],求解装包的最大价值

状态转移方程推导:

假设目前已经有 i-1件物品装在容量为 j 的背包中,并且得到最大价值Package[ i-1 ][ j ],当前装第i件,那么讨论分两个角度,如果第i个物品装不入背包,那么最大价值不变。如果第i个物品可以放入,那么如果背包容量为 j-Volume[i]的时候的最大装包价值加上第i个物品价值大于Package[i-1][j],那么Package[i][j]最大价值就更新了,否则就不装i

得到状态转移方程:

a. Volume[ i ] > j :Package[ i-1 ][ j ]

b. j >= Volume[ i ] :max( Package[ i-1 ][ j-Volume[i] ] +Value[ i ], Package[ i-1 ][ j ] )

实现代码:

#include<iostream>
#include<algorithm>
#include<memory>
using namespace std;
#define MAX_SIZE 1024
int Volume[MAX_SIZE];    /*每个物品所占空间(或重量)*/
int Value[MAX_SIZE];     /*物品价值*/
int Package[MAX_SIZE][MAX_SIZE];   /*未优化,Package[i][j] 表示前i个物品放入容量为j的购物车的最大价值*/
int MAX_Volume, Goods_Num;  /*最大容量,物品数量*/

void Init()
{
	memset(Package, 0, sizeof(Package));
}
void _01Package()
{
	for (int i = 1; i <= Goods_Num; i++)
	{
		for (int j = 1; j <= MAX_Volume; j++)
		{
			if (j < Volume[i])     /*装不下第i个物品*/
				Package[i][j] = Package[i - 1][j];
			else
				Package[i][j] = max(Package[i - 1][j], Package[i - 1][j - Volume[i]] + Value[i]);
				/*将第i个物品放在容量为j - Volume[i](已得的最优情况)的购物车里*/
		}
	}
}
void Print_Res()  /*打印结果*/
{
	cout << "The max value of Package:" << Package[Goods_Num][MAX_Volume] << endl;
	
	int i, j = MAX_Volume;
	cout << "Goods in Package: ";
	for (int i = Goods_Num; i > 0; i--)
	{
		if (Package[i][j] > Package[i - 1][j])
		{
			cout << i<<" ";
			j -= Volume[i];
		}
	}
	cout << endl;
}

优化:

在设计算法时会发现,上一层的数据在本层中并没有修改,而是作为状态的记录,而且本层当前Index只会用到上一层Index之前的数据,所以可以降成一维,称为滚动数组。并且要求每次都重后往前遍历,避免重复叠包

优化代码:

#include<iostream>
#include<algorithm>
#include<memory>
using namespace std;
#define MAX_SIZE 1024
int Volume[MAX_SIZE];    /*每个物品所占空间(或重量)*/
int Value[MAX_SIZE];     /*物品价值*/
int _Package[MAX_SIZE];   /*优化后,Package[i]表示容量i的购物车所获得的最大价值*/
int MAX_Volume, Goods_Num;  /*最大容量,物品数量*/

void Init()
{
	memset(_Package, 0, sizeof(_Package));
}

void _01Package_Optimize()  /*优化算法*/
{
	for (int i = 1; i <= Goods_Num; i++)
		for (int j = MAX_Volume; j >= Volume[i]; j--) /*每个物品最多只能装一次,从后开始遍历防止最优情况叠加*/
			_Package[j] = max(_Package[j], _Package[j - Volume[i]] + Value[i]);
}

void Print_Res_Optimize()
{
	cout << "The max value of Package:" << _Package[MAX_Volume] << endl;
}

  • 完全背包:

概念:

有Goods_Num件物品,MAX_Volume的最大装载量,每种物品数量无限,每种物品都有对应的重量或者说体积Volume[i],价值Value[i],求解装包的最大价值

状态转移方程推导:

与01背包的思路基本相似,不过重要的一点是,每种物品是无限的,所以背包可以多填

得状态转移方程:

a. Volume[ i ] > j Package[i-1][ j ]

b. j>=Volume[ i ] :max( Package[ i-1 ][ j-m*Volume[ i ] ] +m*Value[ i ], Package[ i-1 ][ j ] )

实现代码:

#include<iostream>
#include<algorithm>
#include<memory>
using namespace std;
#define MAX_SIZE 1024
int Volume[MAX_SIZE];    /*每个物品所占空间(或重量)*/
int Value[MAX_SIZE];     /*物品价值*/
int Package[MAX_SIZE][MAX_SIZE];   /*未优化,Package[i][j] 表示前i个物品放入容量为j的购物车的最大价值*/
int MAX_Volume, Goods_Num;  /*最大容量,物品数量*/

void Init()
{
	memset(Package, 0, sizeof(Package));
}

void Total_Package()
{
	for (int i = 1; i <= Goods_Num; i++)
	{
		for (int j = MAX_Volume; j >= Volume[i]; j--)  /*重后往前避免单物品多填影响后序的填包,后填不影响前*/
		{
			int k = j / Volume[i];   /*最多再填k个i物品*/
			for (int m = 0; m <= k; m++)
				Package[i][j] = max(Package[i - 1][j - m * Volume[i]] + m * Value[i], Package[i - 1][j]);
		}
	}
}

优化代码:

#include<iostream>
#include<algorithm>
#include<memory>
using namespace std;
#define MAX_SIZE 1024
int Volume[MAX_SIZE];    /*每个物品所占空间(或重量)*/
int Value[MAX_SIZE];     /*物品价值*/
int _Package[MAX_SIZE];   /*优化后,Package[i]表示容量i的购物车所获得的最大价值*/
int MAX_Volume, Goods_Num;  /*最大容量,物品数量*/

void Init()
{
	memset(_Package, 0, sizeof(_Package));
}

void Total_Package_Optimize()  /*优化算法*/
{
	for (int i = 1; i <= Goods_Num; i++)
	{
		for (int j = Volume[i]; j <= MAX_Volume; j++)/*一维,从前往后dp才能实现一个物品的多填*/
			_Package[j] = max(_Package[j], _Package[j - Volume[i]] + Value[i]);
	}
}

void Print_Res_Optimize()/*打印*/
{
	cout << "The max value of Package:" << _Package[MAX_Volume] << endl;
}


  • 多重背包:

概念:

有Goods_Num件物品,MAX_Volume的最大装载量,第i件物品有数量为Num[i],每种物品都有对应的重量或者说体积Volume[i],价值Value[i],求解装包的最大价值

状态转移方程推导:

其实就是在一个展开的01背包问题,比如有3件相同物品,不如将它展开成 1 1 1 形式,然后01装包

状态转移方程:

a. Volume[ i ] > j :Package[ i-1 ][ j ]

b. j >= Volume[i] :max( Package[ i-1 ][ j-Volume[ i ] ] +Value[i], Package[ i-1 ][ j ] )

只不过,对于物品数大于1的,需要展开Num[i]次

实现代码:

#include<iostream>
#include<algorithm>
#include<memory>
using namespace std;
#define MAX_SIZE 1024
int Volume[MAX_SIZE];    /*每个物品所占空间(或重量)*/
int Value[MAX_SIZE];     /*物品价值*/
int Num[MAX_SIZE];	/*i物品的个数*/
int Package[MAX_SIZE];   /*优化后,Package[i]表示容量i的购物车所获得的最大价值*/
int MAX_Volume, Goods_Num;  /*最大容量,物品数量*/

void Init()
{
	memset(Package, 0, sizeof(Package));
}

void Multi_Package()
{
	for (int i = 1; i <= Goods_Num; i++)
	{
		for (int j = 1; j <= Num[i]; j++)
		{
			for (int k = MAX_Volume; k >= Volume[i]; k--)
				Package[k] = max(Package[k], Package[k - Volume[i]] + Value[i]);
		}
	}
}

void Print_Res()
{
	cout << "The max value of Package:" << Package[MAX_Volume] << endl;
}

优化:

这时候想一个问题,如果一件物品的数量很大很大,那么,对于第三个循环需要跑的时间就很长,在前面说过,多重背包是可拆成01背包形式,那么,何不提前处理 Volume[MAX_SIZE],Value[MAX_SIZE],将问题先转成01背包。

转化时有技巧,运用快速幂的知识,将一件物品拆成 1,2,4.......,当然有个容易想的前提:1~N的数可由2的若干指数和表示

优化代码:

#include<iostream>
#include<algorithm>
#include<memory.h>
using namespace std;
#define MAX_SIZE 1024
int Op_Vol[MAX_SIZE];   /*优化后的物品体积*/
int Op_Val[MAX_SIZE];   /*优化后的物品价值*/
int Package[MAX_SIZE];   /*背包*/
int MAX_Volume,GoodsNum;  /*背包最大体积,原先的货品数量*/
int Op_GoodsNum;   /*优化后的物品数*/
 
void Init()  /*初始化*/
{
    Op_GoodsNum=0;
    memset(Op_Val,0,sizeof(Op_Val));
    memset(Op_Vol,0,sizeof(Op_Vol));
    memset(Package,0,sizeof(Package));
}

void Multi_Package()
{
    for(int i=1;i<=Op_GoodsNum;i++)   /*优化后转变为01背包问题*/
    {
        for(int j=MAX_Volume;j>=Op_Vol[j];j--)
            Package[j]=max(Package[j],Package[j-Op_Vol[i]]+Op_Val[i]);
    }
}

int main()
{
    int vol,val,num;
    while(cin>>GoodsNum>>MAX_Volume)
    {
        Init();
        for(int i=1;i<=GoodsNum;i++)
        {
            cin>>vol>>val>>num;
            for(int j=1;j<=num;j<<=1)   /*优化成01背包问题*/
            {
                Op_Val[++Op_GoodsNum]=j*val;
                Op_Vol[Op_GoodsNum]=j*vol;
                num-=j;
            }
            if(num>0)
            {
                Op_Val[++Op_GoodsNum]=num*val;
                Op_Vol[++Op_GoodsNum]=num*vol;
            }
        }
        Multi_Package();
        cout<<Package[MAX_Volume]<<endl;
    }
    return 0;
}

 

猜你喜欢

转载自blog.csdn.net/SZU_Crayon/article/details/82432909