【ACM】华为oj--购物单(01背包变种)

题目描述:

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

主件 附件
电脑 打印机,扫描仪
书柜 图书
书桌 台灯,文具
工作椅

如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。王强想买的东西很多,为了不超出预算,他把每件物品规定了一个重要度,分为 5 等:用整数 1  5 表示,第 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 行,为两个正整数,用一个空格隔开:N m

(其中 N ( <32000 )表示总钱数, m ( <60 )为希望购买物品的个数。)


从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q


(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1  5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)

输出:

输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值( <200000 )


样例:

1000 5 

800 2 0 

400 5 1 

300 5 1 

400 3 0

 500 2 0

输出:

2200


分析:这个就是01背包问题,但是有一个附加条件是附件只有在其主件已经购买的情况下才能购买

首先先把01背包解释清楚,以前也看过,一直不是很明白,今天搞了一天终于基本理解。

假设有一个背包,可以装重量为W,现在有m件物品,每件物品价值为vi,重量为wi,怎样装能使背包价值最大?

01背包隐含条件是每个物品只有一件,状态只有放或者不放。

定义一个数组c[][],c[i][w]前i件物品放到一个容量为w的背包中可以获得最大价值。那么对于每件物品i,可以分为放或者不放,具体:

(1)如果wi>w,那么不能放入背包,c[i][w]=c[i-1][w];

(2)如果wi<=w,那么可以选择放或者不放,分别对于转移方程:c[i][w]=c[i-1][w-wi]+v[i],c[i][w]=c[i-1][w];取两者中较大值,即

c[i][w]=max{c[i-1][w-wi]+v[i],c[i-1][w]}


好啦,01背包理解了这个题就好办了,两个地方需要改动:

1. 价值是vi*di(di是第i个物品的度)

2. 物品i若是附件,需要判断其主件是否已经加入背包。这里可以用一个矩阵flag[][]记录每个状态下每个物品是否加入背包。如果附件要加入背包,先判断主件是否在w-wi的背包里,嗯,这里要注意一下。


#include 
#include 
#include 
#include 

#define MAX_N 32000
#define MAX_M 60

using namespace std;

int Knapsack(int v[],int w[],int pro[],int N,int m)
{
    int c[MAX_M][MAX_N],i,j;
    bool flag[MAX_M][MAX_N];
    memset(c,0,sizeof(c));
    memset(flag,false,sizeof(flag));
    for(i=1;i<=m;i++)
    {
        for(j=0;j<=N;j++)
        {
            if(pro[i]==0)
            {
                c[i][j]=c[i-1][j];
                if(v[i]<=j&&c[i][j]


你可能感兴趣的:(ACM)