一道可用背包问题解的华为编程题

题目描述

王强今天很开心,公司发给N元的年终奖。王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:

主件

附件

电脑

打印机,扫描仪

书柜

图书

书桌

台灯,文具

工作椅

如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。王强想买的东西很多,为了不超出预算,他把每件物品规定了一个重要度,分为 5 等:用整数 1 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是 10 元的整数倍)。他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

    设第 j 件物品的价格为 v[j] ,重要度为 w[j] ,共选中了 k 件物品,编号依次为 j 1  j 2 …… j k ,则所求的总和为:v[j 1 ]*w[j 1 ]+v[j 2 ]*w[j 2 ]+ … +v[j k ]*w[j k ] 。(其中 * 为乘号)

    请你帮助王强设计一个满足要求的购物单。


这个问题我的解答思路是:

用一个二维数组先接收要放入的物品数据信息,第一列用于区分主件还是附件,第二列价值,第三列重要度

该题属于有依赖的背包问题,这只是这其中的一个简单的有依赖的背包问题。对于这类的解法我是参考了网上“背包问题九讲”。可先转化为分组背包问题,先对这些物品进行分组。

对于该题,因为只有先选主件才能选择相应的附件。

可先分类,例:电脑、打印机、扫描仪这一类,可得到电脑、电脑与打印机、电脑与扫描仪、电脑与扫描仪与打印机这几个组合。可把每一个组合看成一个物品构成一组。剩下的其余物品一样。这样可得到好多组。然后利用下面的方法求解。

一道可用背包问题解的华为编程题_第1张图片

对于该题,求解的是每件物品的价格与重要度的乘积的总和最大,所以上面的f[k][v]表示前k组物品花费费用v能取得的乘积的总和最大值。


注意下面的这个容易出错。

一道可用背包问题解的华为编程题_第2张图片

(也可能信息储存的结构设计的不好,没仔细思考其他思路)首先是忘记if判断语句,因为要判断当前的物品是否会超出当前的总钱数。若没有这条语句也可执行,但会数组越界,得出的是任意值。

还有就是重要度的处理,在选中物品后再加上重要度,得到的要求的量放入数组存储。

再者是没有写sum,这样导致的结果是每个物品组只得到n=N的值。(因为物品组是顺序存放在一个二维数组中的)

对于这个程序的调试完全是在一些位置放入输出函数,显示想要查看的值,分析达到预期效果没,没有的话分析原因。

因为此题的附件数量不多,可以想法使程序简单点。(而且此程序也没有对附件先进行01背包优化。而且第二个for循环的下限似乎可以用主件的值代替,因为一个组中主件的值最小。)

下面为自己写的一个程序:(因为C++还在学习中,虽然输入输出用的C++的函数,但实际也相当于用C写的)

#include 
using namespace std;

#define max(x,y) (x)>(y)?(x):(y)
int main()
{
	int N, m;//N ( <32000 )表示总钱数, m ( <60 )为希望购买物品的个数。
	int v, p, q; //其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~5 ), q 表示该物品是主件还是附件。
	            //如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号
	int group[60 +20][3] = {0};//用于存放所得的物品组数据信息,第一列存新组合的价值,第二列存所属物品组,也即是哪个主件,第三列存乘以重要度后的值
	int material[60][3] = { 0 };//用于存放物品的信息,第一列用于区分主件还是附件,第二列价值,第三列重要度

	int j = 0;//用于计算是第几个主件,最终的数量也即是物品组的数量
	int k=0;//用于说明是该主件的第几个附件
	int sum=0;//用于计算化成物品组后总的产生的物品数量

	int dq[32000] = {0};

	cin >> N >> m;
		//用下法先储存物品的数据信息
		for (int i = 0; i < m; i++)
		{
			cin >> v >> p >> q;
			material[i][0] = q;
			material[i][1] = v;
			material[i][2] = p;
		}
		//动态规划开始求解
		//因为每个主件的附件的数量最多2个,故直接与主件进行组合,没有用01背包问题求解

		for (int i = 0; i < m; i++)
		{
			if (material[i][0] == 0)
			{
				j++;//用于计算是第几个主件,最终的数量也即是物品组的数量
				group[sum][1] = j;
				//		group[sum][0] = material[i][1] * material[i][2];
				group[sum][2] = material[i][1] * material[i][2];
				group[sum][0] = material[i][1];
				k = 0;
				sum++;
			}
			else
			{
				k++;//用于计算是该主件的第几个附件
				if (k != 2)
				{
					group[sum][1] = j;//赋值所属主件material[i][0]应与j是一样的
					//	group[sum][0] = material[i][1] * material[i][2] + group[sum - 1][0];//附件1与主件
					group[sum][2] = material[i][1] * material[i][2] + group[sum - 1][2];
					group[sum][0] = material[i][1] + group[sum - 1][0];
				}
				else
				{
					group[sum][1] = j;//赋值所属主件
					//	group[sum][0] = material[i][1] * material[i][2] + group[sum - 2][0];//附件2与主件
					group[sum][2] = material[i][1] * material[i][2] + group[sum - 2][2];
					group[sum][0] = material[i][1] + group[sum - 2][0];
					sum++;
					group[sum][1] = j;//赋值所属主件
					//	group[sum][0] = material[i][1] * material[i][2] + group[sum - 2][0];//附件1、2与主件
					group[sum][2] = material[i][1] * material[i][2] + group[sum - 2][2];
					group[sum][0] = material[i][1] + group[sum - 2][0];
				}
				sum++;
			}
		}
		sum = 0;//清零,用于下面的循环
		//对物品组进行01背包问题求解
		for (int i = 1; i <= j; i++)
		{
			for (int n = N; n > 0; n--)//想法如何优化下限,其实不用到零
			{

				k = 0;
				while (group[sum][1] == i)//用于寻找单个物品中最优的解
				{
					k++;//用于计算循环次数
					if (n - group[sum][0] >= 0)
						dq[n] = max(dq[n], dq[n - group[sum][0]] + group[sum][2]);
					sum++;
				}
				sum = sum - k;//用于本物品组的,开始其他金额的下次计算
			}
			sum = sum + k;//计算下个物品组
		}
		cout << dq[N] << endl;
//	while (1);
	return 0;
}



你可能感兴趣的:(C)