【题解】NOIP普及组 金明的预算方案

NOIP普及组 金明的预算方案

  • NOIP普及组 金明的预算方案
    • 题目
    • 考点
    • 思路
    • 代码
      • 参考代码01
      • 参考代码02

题目

原题链接
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 附件
电脑 打印机,扫描仪
书柜 图书
书桌 台灯,文具
工作椅 无
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有0个、1个或2个附件。附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是10元的整数倍)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为:v[j1]w[j1]+v[j2]*w[j2]+ …+v[jk]*w[jk]。(其中为乘号)请你帮助金明设计一个满足要求的购物单。

考点

  • 01背包
  • 有依赖的背包问题
  • 动态规划

思路

其实如果把这道题的依赖关系去掉,那么就是一个完完全全的01背包问题
那么我们可以考虑把每一个附件当成一个主件来买,即把其价格变成主件+附件,重要度同理,于是我们可以得到参考代码01
这不失为一种好做法,而且在NOIP极其坑逼的样例下是差不出错的!
但是当你敲入如下样例时,奇迹就会出现:

10000 2
1000 1 0
2000 1 1

此时用眼睛都能看出来肯定是3000正解,但是参考代码01却得出了4000?!?!
原因十分简单,因为你在买2000 1 1的附件时,你得到的其实是主件+附件,再加上之前买过的主件,BOOM!
这个问题对于我的代码来说十分简单,对于每个主件和附件组的一部分先不存入dp[]数组,而是存入save[],当这个主件以及其附件全部dp完毕后,把save[]存进dp[]中
那些开一堆if的代码,我没做过,不知道,但是貌似一堆if的代码敲入那个数据后有些是对的(玄学23333)
至于我的代码为何能够如此简单,看参考代码02注释呗~

代码

参考代码01

#include
#include
#include
using namespace std;

int weight[101][4];
int value[101][4];
int mmp[101];
int dp[33000];
int save[33000];
int totmmp;
int n,m;

int main()
{
    int v,p,q;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&v,&p,&q);
        if(q==0)
        {
            weight[++totmmp][0]=v;
            value[totmmp][0]=p*v;
            mmp[i]=totmmp;
        }
        else
        {
            int father=mmp[q];
            if(value[father][1]==0)
            {
                value[father][1]=p*v+value[father][0];
                weight[father][1]=v+weight[father][0];
            }
            else
            {
                value[father][2]=p*v+value[father][0];
                weight[father][2]=v+weight[father][0];

                value[father][3]=value[father][2]+value[father][1]-value[father][0];
                weight[father][3]=weight[father][2]+weight[father][1]-weight[father][0];
            }
        }
    }

    int t;
    for(int i=1;i<=totmmp;++i)
    {
        for(int k=0;k<=3;++k)
        {
            if(value[i][k]==0) break;
            for(int j=n;j>=weight[i][k];--j)
            {
                if(dp[j-weight[i][k]] or j-weight[i][k]==0)
                {
                    dp[j]=max(dp[j],dp[j-weight[i][k]]+value[i][k]);
                }
            }
        }
    }
    int ans=-1;
    for(int i=1;i<=n;++i)
    {
        ans=max(dp[i],ans);
    }
    cout<

参考代码02

#include
#include
#include
using namespace std;

int weight[101][4];//weight[i][j]表示当主件为第i个主件时,j=0表示只买主件,j=1表示买第一个附件,j=2表示买第二个附件,j=3表示两个附件都买
int value[101][4];//value[i][j]同理
int mmp[101];//存放输入数据中的第i行表示在本程序中的mmp[i]号主件
int dp[33000];//01背包标准
int save[33000];//相当于dp[]
int totmmp;//一个几个主件
int n,m;

int main()
{
    int v,p,q;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&v,&p,&q);
        if(q==0)//是主件
        {
            weight[++totmmp][0]=v;
            value[totmmp][0]=p*v;
            mmp[i]=totmmp;
        }
        else//是附件
        {
            int father=mmp[q];
            if(value[father][1]==0)//没有附件
            {
                value[father][1]=p*v+value[father][0];
                weight[father][1]=v+weight[father][0];
            }
            else//已经有一个附件
            {
                //根据value[][],weight[][]的定义,有两个附件就一定会有两个合在一起的解
                value[father][2]=p*v+value[father][0];
                weight[father][2]=v+weight[father][0];

                value[father][3]=value[father][2]+value[father][1]-value[father][0];
                weight[father][3]=weight[father][2]+weight[father][1]-weight[father][0];
            }
        }
    }

    int t;
    for(int i=1;i<=totmmp;++i)//标准01背包第一层
    {
        //k,j枚举合起来相当于01背包第二层
        for(int k=0;k<=3;++k)//枚举购买情况
        {
            if(value[i][k]==0) break;
            for(int j=n;j>=weight[i][k];--j)//标准01背包第二层
            {
                if(dp[j-weight[i][k]] or j-weight[i][k]==0)
                {
                    //缓存
                    t=max(dp[j],dp[j-weight[i][k]]+value[i][k]);
                    save[j]=max(save[j],t);
                }
            }
        }
        for(int j=1;j<=n;++j) dp[j]=save[j];
    }
    int ans=-1;
    for(int i=1;i<=n;++i)
    {
        ans=max(dp[i],ans);
    }
    cout<

你可能感兴趣的:(+++++算法+++++,———基础算法———,dp,----01背包)