机试算法讲解: 第56题 多重背包之一起支援灾区

/*
多重背包:介于0-1背包和完全背包之间。容积为V的背包,给定一些物品,每种物品包含体积w,价值v和数量k,求用该背包能装下的最大价值总量。
         每种武平可选的数量不再为无穷或者1,而是一个确定的数k。将多重转换为0-1背包。即每种物品被视为k种不同物品,对所有物品求0-1背包。
		 将原数量为k的物品拆分为若干组,每组物品包含的原物品个数分别为:1,2,4,...,k-2的c+1次方,其中c为使k-2的c+1次方>0的最大整数。类似与
		 二进制拆分。可以得到0到k之间任意物品价值重量和。对所有这些信物品做0-1背包。

问题:支援灾区。你有n元,市场有m种大米,每种打磨都是袋装产品,价格不等,并且只能整袋购买。你最多能采购多少公斤粮食
输入:第一行1个正整数C(表示有C组测试用例),每组测试用例的第一行时两个整数n和m(1<=n<=100,1<=m<=100),分别表示经费的金额和大米的种类,然后是m行数据。
     每行包括3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数。
输出:输出能够购买大米最多重量,经费可以不用完

输入:
1
8 2
2 100 4
4 100 2
输出:
400

关键:
1多重背包转化为0-1背包后,采用逆序
2 背包问题:外层循环是物体,内层循环是总容积,对每个物品求其价值总和
3 拆分方法:
          while(k-c>0)
		  {
		     k -= c ; 
			 list[++iCnt].v = v*c;
			 list[iCnt].w = w*c;
			 c *= 2
		   }
		   //注意最后一次拆分是用k乘不是用c乘
		   list[++iCnt].v = k*v;
		   list[iCnt].w = k*w;
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int max(int a,int b)
{
	return a>b ? a:b;
}

typedef struct List
{
	int w;//体积
	int v;//价格
}List;

int main(int argc,char* argv[])
{
	int T;
	int s,n;
	int i,j;
	int w,v,k;
	while(scanf("%d",&T))
	{
		while(T--)
		{
			List list[2001];
			int dp[101];//总共最多100元,相当于容积
			scanf("%d %d",&s,&n);
			int iCnt = 0;//用于计数拆分后的物品总数
			for(i = 0 ; i < n ; i++)
			{
				scanf("%d %d %d",&w,&v,&k);
				//开始进行多重背包的拆分
				int c = 1;
				while(k - c > 0)
				{
					k -= c;//拆分
					list[++iCnt].v = c*v;
					list[iCnt].w = c*w;
					c *= 2;
				}
				//易错,最后一次的k小于c,也要拆分,这里是乘以k倍而不是c倍
				list[++iCnt].v = k*v;
				list[iCnt].w = k*w;
			}
			//背包初始化,s为总体积
			for(i = 0 ; i <= s; i++)
			{
				dp[i] = 0;
			}
			//背包计算,cnt为物品数,s为总体积
			for(i = 1 ; i <= iCnt ; i++)
			{
				for(j = s; j >= list[i].w ; j--)
				{
					dp[j] = max(dp[j],dp[j-list[i].w] + list[i].v);
				}
			}
			printf("%d\n",dp[s]);
		}
	}
	system("pause");
	getchar();
	return 0;
}

你可能感兴趣的:(多重背包,机试算法)