华为oj 购物单

王强今天很开心,公司发给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 ] 。(其中 * 为乘号)
    请你帮助王强设计一个满足要求的购物单。
 


知识点 字符串
运行时间限制 0M
内存限制 0
输入
输入的第 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背包问题,但是有一个附加条件是附件只有在其主件已经购买的情况下才能购买。

假设有一个背包,可以装重量为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 <stdio.h>
#include <iostream>
#include <cstring>
#include <stdlib.h>


#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]<c[i-1][j-v[i]]+v[i]*w[i])
                {
                    c[i][j]=c[i-1][j-v[i]]+v[i]*w[i];
                    flag[i][j]=true;
                }
            }
            else
            {
                    c[i][j]=c[i-1][j];
                    if(v[i]<=j&&c[i][j]<c[i-1][j-v[i]]+v[i]*w[i])
                    {
                        if(flag[pro[i]][j-v[i]])
                            c[i][j]=c[i-1][j-v[i]]+v[i]*w[i];
                    }
            }
        }
    }
    return c[m][N];
}




int main()
{
   // freopen("in.txt","r",stdin);
int v[MAX_M],w[MAX_M],pro[MAX_M],N,m;
scanf("%d%d",&N,&m);
for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&v[i],&w[i],&pro[i]);
    }
    printf("%d\n",Knapsack(v,w,pro,N,m));
    return 0;
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值