背包问题-敲代码:01背包、完全背包、多重背包、混合背包、二维费用、分组背包、恰好背包

1、01背包

        物品要么放,要么不放,即每种物品最多可以放一个,求最大收益。

/*
	输入:
		n            物品个数
		m            背包容量
		w[0] v[0]    重量,价值
		w[1] v[1]    重量,价值
		* 
		w[n-1] v[n-1]    重量,价值
	输出:
		dp[m]			 最大价值
*/
/*
	输入1:
		3 
		10
		2 4
		4 7
		7 6
	输入2:
		3 
		10
		2 7
		4 5
		7 6
*/

#include 
#include 
using namespace std;

int main()
{
	//输入
	int n;                   //物品个数
	int m;                   //背包容量
	cin>>n>>m

	vector w(n);        //重量
	vector v(n);        //价值
	for(int i=0; i>w[i]>>v[i];
	}
	
	//dp,N*M
	vector dp(m+1, 0);    //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
	for(int i=0; i=w[i]; j--)
		{
			dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
		}
	}
	
	//输出
	cout << dp[m] << endl;     //输出结果

	return 0;
}

2、完全背包

        每种物品个数不限,求最大收益。

/*
	完全背包:每种物品个数不限
	输入:
		n            物品个数
		m            背包容量
		w[0] v[0]    重量,价值
		w[1] v[1]    重量,价值
		* 
		w[n-1] v[n-1]    重量,价值
	输出:
		dp[m]			 最大价值
*/
/*
	输入1:
		3 
		10
		2 4
		4 7
		7 6
	输出:
		20
	输入2:
		3 
		10
		2 7
		4 5
		7 6
	输出:
		35
	输入3:
		5 
		10
		5 1
		4 2
		3 3
		2 4
		1 5
	输出:
		50
*/
#include 
#include 
using namespace std;

int main()
{
	//输入
	int n;                  //物品类数
	int m;                  //背包容量
	cin>>n>>m;
	
	vector w(n);      //重量
	vector v(n);      //价值
	for(int i=0; i>w[i]>>v[i];
	}
	
	//dp
	
	/* 方式一,N*K*M
	//判断一次多少件物品能够获得最大收益
	vector dp(m+1, 0);      //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
	for(int i=0; i=w[i]; j--)
		{
			for(int k=1; k*w[i]<=j; k++)    //多少件物品能够获得最大值
			{
				dp[j] = max(dp[j], dp[j-k*w[i]] + k*v[i]);
			}
		}
	}
	//*/
	
	/* 方式二,N*K*M
	//重复 K=m/w[i] 次 01背包
	vector dp(m+1, 0);      //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
	for(int i=0; i=w[i]; j--)
			{
				dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
			}
		}
	}
	//*/

	/* 方式三,N*K*M
	//计算每种物品数量上限,扩充物品数量
	int nn = n;
	for(int i=0; i dp(m+1, 0);      //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
	for(int i=0; i=w[i]; j--)
		{
			dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
		}
	}
	//*/
	
	//* 方式四,N*M
	//至少放一个,比较当前行,非前一行
	vector dp(m+1, 0);   //把0个物品放入背包,获得的价值为0
	for(int i=0; i

3、多重背包

        每种物品个数有限,求最大收益。

/*
	多重背包:每种物品个数有限
	输入:
		n                       物品个数
		m                       背包容量
		w[0] v[0] c[0]          重量,价值,数量
		w[1] v[1] c[1]          重量,价值,数量
		* 
		w[n-1] v[n-1] c[n-1]    重量,价值,数量
	输出:
		dp[m]			 最大价值
*/
/*
	输入1:
		3 
		10
		2 4 2
		4 7 3
		7 6 4
	输出:
		18
	输入2:
		3 
		10
		2 7 3
		4 5 2
		7 6 1
	输出:
		26
	输入3:
		5 
		10
		5 1 1
		4 2 2
		3 3 1
		2 4 2
		1 5 1
	输出:
		16
*/
#include 
#include 
using namespace std;

int main()
{
	//输入
	int n;                  //物品类数
	int m;                  //背包容量
	cin>>n>>m;
	
	vector w(n);      //重量
	vector v(n);      //价值
	vector c(n);      //数量
	for(int i=0; i>w[i]>>v[i]>>c[i];
	}
	
	//dp
	
	/* 方式一,N*K*M
	//判断一次多少件物品能够获得最大收益
	vector dp(m+1, 0);      //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
	for(int i=0; i=w[i]; j--)
		{
			for(int k=1; k*w[i]<=j && k<=c[i]; k++)    //当前容量下,多少件本物品能够获得最大值
			{
				dp[j] = max(dp[j], dp[j-k*w[i]] + k*v[i]);
			}
		}
	}
	//*/
	
	/* 方式二,N*K*M
	//每件物品进行 K 次01背包
	vector dp(m+1, 0);      //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
	for(int i=0; i=w[i]; j--)
			{
				dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
			}
		}
	}
	//*/

	/* 方式三,N*K*M,K是平均每个物品限制
	//计算每种物品数量上限,扩充物品数量,增加了内存,不如方式二
	int nn = n;
	for(int i=0; i dp(m+1, 0);      //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
	for(int i=0; i=w[i]; j--)
		{
			dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
		}
	}
	//*/
	
	//* 方式四,N*log(K)*M
	//在方式二基础上,二进制简化,减少了k层的判断次数。
	//把多个本种物品看成一个物品,体积、价值加倍,但是组合起来同样可以达到在限制范围内的任意数量。
	vector dp(m+1, 0);
	for(int i=0; i= m)
		{
			for(int j=w[i]; j<=m; j++)
			{
				dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
			}
			continue;
		}

		//否则,是多重背包,简化写法
		int number = c[i];   //物品数限制,剩余物品数
		for(int k=1; k<=number ; k*=2)         //放入 1,2,4,...,kk 个“本种物品”,且 sum = 1+2+4+...+kk <= number,然后判断剩下的 rest = number-sum 件物品
		{
			for(int j=m; j>=k*w[i]; j--)
			{
				dp[j] = max(dp[j], dp[j-k*w[i]] + k*v[i]);
			}
			number -= k;    //剩余物品数,判断能否继续取k个本种物品
		}
		for(int j=m; j>=number*w[i]; j--)
		{
			dp[j] = max(dp[j], dp[j-number*w[i]] + number*v[i]);
		}
	}
	//*/

	//输出
	cout << dp[m] << endl;
	return 0;
}

4、混合背包

        每种物品个数可以是1个,也可以是K个,也可以是无限个(用0表示)。

        题解使用了方式二,K次01的思路,记住方式二。

        对于无限个、限制无效的,退化为完全背包,1个正序。

        对于有限个、限制1个的,转化为多重背包,k个逆序。

/*
	混合背包:每种物品个数可以是1个,也可以是K个,也可以是无限个(用0表示)

	输入:
		n                       物品个数
		m                       背包容量
		w[0] v[0] c[0]          重量,价值,数量
		w[1] v[1] c[1]          重量,价值,数量
		* 
		w[n-1] v[n-1] c[n-1]    重量,价值,数量
	输出:
		dp[m]			 最大价值
*/
/*
	输入1:
		3 
		10
		2 4 1
		4 7 0
		7 6 4
	输出:
		18
	输入2:
		3 
		10
		2 7 3
		4 5 0
		7 6 1
	输出:
		26
	输入3:
		5 
		10
		5 1 1
		4 2 2
		3 3 0
		2 4 2
		1 5 1
	输出:
		16
*/
#include 
#include 
using namespace std;

int main()
{
	//输入
	int n;                  //物品类数
	int m;                  //背包容量
	cin>>n>>m;
	
	vector w(n);      //重量
	vector v(n);      //价值
	vector c(n);      //数量
	for(int i=0; i>w[i]>>v[i]>>c[i];
	}
	
	//dp
	//* 方式一,N*K*M
	//每件物品进行 K 次01背包
	vector dp(m+1, 0);      //一维数组,相当于第-1行(0个物品/没有物品)为全为0;0个物品,价值为0。
	for(int i=0; i= m)      //无限个,或者,限制无效,退化成完全背包
		{
			for(int j=w[i]; j<=m; j++)                 //完全背包,1次正序
			{
				dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
			}
		}
		else                                 //有限个,或者,1个,按多重背包
		{
			for(int k=1; k<=c[i] && k<=m/w[i]; k++)    //多重背包,K次01物品,K次逆序
			{
				for(int j=m; j>=w[i]; j--)
				{
					dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
				}
			}
		}
	}
	//*/
	
	/* 方式二,N*log(K)*M
	vector dp(m+1, 0);
	for(int i=0; i= m)
		{
			for(int j=w[i]; j<=m; j++)
			{
				dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
			}
			continue;
		}

		//否则,是多重背包
		int number = c[i];   //物品数限制
		for(int k=1; k<=number; k*=2)     //放入 1,2,4,...,kk 个“本种物品”,且 sum = 1+2+4+...+kk <= number,然后判断剩下的 rest = number-sum 件物品
		{
			for(int j=m; j>=k*w[i]; j--)
			{
				dp[j] = max(dp[j], dp[j-k*w[i]] + k*v[i]);
			}
			number -= k;     //剩余物品数,判断是否能继续取k件“本种物品”
		}
		for(int j=m; j>=number*w[i]; j--)
		{
			dp[j] = max(dp[j], dp[j-number*w[i]] + number*v[i]);
		}
	}
	//*/

	//输出
	cout << dp[m] << endl;
	return 0;
}

5、二维费用背包

        每种物品有两个费用。

/*
	二维费用背包:每种物品有两个费用。
	
	二维费用01背包:
	二维费用完全背包:逆序 改为 正序
	二维费用多重背包:加一个“物品数量层”,进行K次“二维费用01背包”。并且加入“退化为完全背包”的判断。
	二维费用混合背包:
	
	
	输入:
		n                       物品个数
		m1                      背包容量1
		m2                      背包容量2
		w1[0] w2[0] v[0]          重量1,重量2,价值
		w1[1] w2[1] v[1]          重量1,重量2,价值
		* 
		w1[n-1] w2[n-1] v[n-1]    重量1,重量2,价值
	输出:
		dp[m1][m2]	            最大价值
*/
/*
	输入1:
		3
		10
		10
		2 3 4
		4 2 7
		7 4 6
	输出:
		11
*/
#include 
#include 
using namespace std;

//二维费用01背包
int main()
{
	//输入
	int n;                  //物品类数
	int m1;                 //背包容量1
	int m2;                 //背包容量2
	cin>>n>>m1>>m2;
	
	vector w1(n);      //费用1
	vector w2(n);      //费用2
	vector v(n);       //价值
	
	for(int i=0; i> w1[i] >> w2[i] >> v[i];
	}
	
	//dp
	vector> dp(m1+1, vector(m2+1, 0));    //全部初始化为0
	for(int i=0; i=w1[i]; j1--)     //费用1,逆序
		{
			for(int j2=m2; j2>=w2[i]; j2--)     //费用2,逆序
			{
				dp[j1][j2] = max(dp[j1][j2], dp[j1-w1[i]][j2-w2[i]] + v[i]);
			}
		}
	}

	//输出
	cout << dp[m1][m2] << endl;
	
	return 0;
}

//二维费用混合背包
int main_mixbag()
{
	//输入
	int n;                  //物品类数
	int m1;                 //背包容量1
	int m2;                 //背包容量2
	cin>>n>>m1>>m2;
	
	vector w1(n);      //费用1
	vector w2(n);      //费用2
	vector v(n);       //价值
	vector c(n);       //数量(0代表无限)
	
	for(int i=0; i> w1[i] >> w2[i] >> v[i] >> c[i];
	}
	
	//dp
	vector> dp(m1+1, vector(m2+1, 0));    //全部初始化为0
	for(int i=0; i= m1 && c[i]*w2[i] >= m2))      //无限个、限制无效,退化为完全背包,正序
		{
			for(int j1=w1[i]; j1<=m1; j1++)
			{
				for(int j2=w2[i]; j2<=m2; j2++)
				{
					dp[j1][j2] = max(dp[j1][j2], dp[j1-w1[i]][j2-w2[i]] + v[i]);
				}
			}
		}
		else                                                         //有限个,多重背包(加一个数量层),逆序
		{
			for(int k=1; k<=c[i] && k<=m1/w1[i] && k<=m2/w2[i]; k++)
			{
				for(int j1=m1; j1>=w1[i]; j1--)     //费用1,逆序
				{
					for(int j2=m2; j2>=w2[i]; j2--)     //费用2,逆序
					{
						dp[j1][j2] = max(dp[j1][j2], dp[j1-w1[i]][j2-w2[i]] + v[i]);
					}
				}
			}
		}
	}

	//输出
	cout << dp[m1][m2] << endl;
	
	return 0;
}

6、分组背包

        每组中只能选一个。

        题解使用了方式一,“同种物品放几个“、”分组中放哪个“ 可以获得最大收益的思路,记住方式一。

/*
分组背包问题

	* 每组中选一件
	* 有 N 件物品和一个容量为 M 的背包。第i件物品的费用是w[i],价值是v[i]。
	* 这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
		
		dp[k][j] 表示 前k组物品 花费费用j 能取得的最大权值
		dp[k][j] = max(dp[k−1][j], dp[k−1][j−w[i]] + v[i]);   物品i属于组k
		dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
		
		for(k=kmin; k<=kmax; k++)   //分组
			for(j=m; j>=0; j--)     //容量
				for(i)              //分组的物品,i属于分组k
				
					if(j>w[i])
						dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
	
	输入:
		n                       物品个数
		m                       背包容量
		w[0] v[0] k0            重量,价值,分组
		w[1] v[1] k1            重量,价值,分组
		* 
		w[n-1] v[n-1] k[n-1]    重量1,重量2,价值
	输出:
		dp[m]			 最大价值
*/
/*
	输入1:
		6
		10
		2 1 1
		3 2 1
		4 2 2
		5 3 2
		2 4 3
		1 3 3
	输出:
		9
*/
#include 
#include 
#include 
using namespace std;

//二维费用01背包
int main()
{
	//输入
	int n;                  //物品类数
	int m;                  //背包容量
	cin>>n>>m;
	
	vector w(n);           //费用
	vector v(n);           //价值
	map> mp;   //分组,物品编号序列
	
	for(int i=0; i> w[i] >> v[i] >> k;
		mp[k].push_back(i);
	}
	
	int kmin = mp.begin()->first;           //最小分组号
	int kmax = (--mp.end())->first;         //最大分组号
	
	//dp
	vector dp(m+1, 0);
	for(int k=kmin; k<=kmax; k++)//分组
	{
		for(int j=m; j>=0; j--)   //费用1,逆序
		{
			for(auto i : mp[k])     //分组k的物品编号,数组。当前分组,当前容量下,放入哪种物品能够获得最大收益。和完全背包很像,完全背包是放入几个物品能够获得最大收益。
			{
				if(j>=w[i])
					dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
			}
		}
	}

	//输出
	cout << dp[m] << endl;
	
	return 0;
}

7、恰好背包

        所选则的物品需要刚好装满背包,求最大收益。

        只需要改变初始化即可。

	//非恰好背包,初始化
	vector dp(m+1, 0);     //0个物品,任何容量,收益为0

	//恰好背包,初始化
	vector dp(m+1, INT_MIN);     //0个物品,不能装满其他位置,非法。
	dp[0] = 0;                        //0个物品,恰好可以装满空背包,收益为0

        计算之后,在 dp 数组结果中,INT_MIN 的为非法。

 

好链接

1、动态规划:完全背包、多重背包

https://blog.csdn.net/qq_38984851/article/details/81133840

2、背包九讲——全篇详细理解与代码实现

https://blog.csdn.net/yandaoqiusheng/article/details/84782655

你可能感兴趣的:(#,算法题,-,背包问题,#,算法题,-,动态规划)