zoukankan      html  css  js  c++  java
  • P1064 金明的预算方案

    题目描述

    金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NN元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:

    主件 附件

    电脑 打印机,扫描仪

    书柜 图书

    书桌 台灯,文具

    工作椅 无

    如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有00个、11个或22个附件。附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的NN元。于是,他把每件物品规定了一个重要度,分为55等:用整数1-515表示,第55等最重要。他还从因特网上查到了每件物品的价格(都是1010元的整数倍)。他希望在不超过NN元(可以等于NN元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

    设第jj件物品的价格为v_[j]v[j],重要度为w_[j]w[j],共选中了kk件物品,编号依次为j_1,j_2,…,j_kj1,j2,,jk,则所求的总和为:

    v_[j_1] imes w_[j_1]+v_[j_2] imes w_[j_2]+ …+v_[j_k] imes w_[j_k]v[j1]×w[j1]+v[j2]×w[j2]++v[jk]×w[jk]。

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

    输入输出格式

    输入格式:

    11行,为两个正整数,用一个空格隔开:

    N mNm (其中N(<32000)N(<32000)表示总钱数,m(<60)m(<60)为希望购买物品的个数。) 从第22行到第m+1m+1行,第jj行给出了编号为j-1j1的物品的基本数据,每行有33个非负整数

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

    输出格式:

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

    有附属关系的背包问题

    还好这题最多两个附属品  那么就是五种状态转移

    不选  只选主件  主件加附件1 主件加附件2 主件加所有附件

    注意要大于体积才能转移!!因为这个wa了

    #include<bits/stdc++.h>
    using namespace std;
    //input
    #define rep(i,x,y) for(int i=(x);i<=(y);++i)
    #define RI(n) scanf("%d",&(n))
    #define RII(n,m) scanf("%d%d",&n,&m);
    #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
    #define RS(s) scanf("%s",s)
    #define LL long long
    #define REP(i,N)  for(int i=0;i<(N);i++)
    #define CLR(A,v)  memset(A,v,sizeof A)
    //////////////////////////////////
    #define N 65
    #define inf -0x3f3f3f3f
    
    int zhuv[N];
    int zhuc[N];
    int fuv[N][3];
    int fuc[N][3];
    int dp[32005];
    int main()
    {
      int m,n;
      RII(m,n);
      rep(i,1,n)
      {
          int a,b,c;
          RIII(a,b,c);
          if(!c)
          {
              zhuv[i]=a;
              zhuc[i]=a*b;
          }
          else
          {
              fuv[c][0]++;
              fuv[c][fuv[c][0]]=a;
              fuc[c][fuv[c][0]]=a*b;
          }
      }
      rep(i,1,n)
      for(int j=m;zhuv[i]&&j>=zhuv[i];--j)
      {
          dp[j]=max(dp[j],dp[j-zhuv[i] ]+zhuc[i] );
        
         if(j>=zhuv[i]+fuv[i][1])
          dp[j]=max(dp[j],dp[j-zhuv[i]-fuv[i][1] ]+zhuc[i]+fuc[i][1]      );
        
         if(j>=zhuv[i]+fuv[i][2])
           dp[j]=max(dp[j],dp[j-zhuv[i]-fuv[i][2] ]+zhuc[i]+fuc[i][2]     );
         
         if(j>=zhuv[i]+fuv[i][1]+fuv[i][2])
          dp[j]=max(dp[j],dp[j-zhuv[i]-fuv[i][1]-fuv[i][2] ]+zhuc[i]+fuc[i][1] +fuc[i][2]      );
      }
      cout<<dp[m];
    
    }
    View Code

    但是很多情况下状态转移不止这几种

    得使用背包九讲里面的普适方法!!! 

    可以先对每种主件的 附件的集合 进行一次 0101 背包处理,就可以先求出 对于每一种主件包括其附件的组合中,每种花费的最大价值(读不懂的同学可以看后面解释)。

    对于每一种主件的01背包处理:

    for i:主件k的所有附件
        for j:价值(0 ~ n-主件价值)
            01背包递推

    这样可以得到主件 kk 的附件中费用依次为 0 sim n-v[k]0nv[k] 时的相应最大价值 f[0 sim n-v[k]]f[0nv[k]],那么我们就得到了主件 kk 及其附件集合的 n-v[k]+1nv[k]+1 种不同选择情况,其中费用为 v[k]+tv[k]+t 的物品的价值就是 f[t]+v[k]*p[k]f[t]+v[k]p[k] 。

    对于每一个主件处理出的情况,在 n-v[k]+1nv[k]+1 种情况之中只能最多选择一种选入最终答案之中(把上面文字多读几遍吧),原问题便转化成一个分组背包问题。

    如果你不知道分组背包的话:

    for 所有的组k
        for v = V ... 0
            for 所有的i属于组k
                f[v]=max{f[v],f[v-c[i]]+w[i]}

    这题的分组背包部分应该是这样写的:

    for 所有的主件数k
        for j = n ... 0
            for 所有的主件和附件的组合属于组k
                f[j]=max{f[j],f[j-v[i]]+v[i]*p[i]}


    #include<bits/stdc++.h>
    using namespace std;
    //input
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define RI(n) scanf("%d",&(n))
    #define RII(n,m) scanf("%d%d",&n,&m);
    #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
    #define RS(s) scanf("%s",s);
    #define LL long long
    #define REP(i,N)  for(int i=0;i<(N);i++)
    #define CLR(A,v)  memset(A,v,sizeof A)
    //////////////////////////////////
    #define N 100
    struct
    {
        int v,c;
        int id;
    }a[N],fu[N][N];//fu为主件i的第j个附件
    
    int num[N];//附件个数
    int c[N][N];//第i组第j个物品的价值
    int v[N][N];
    int dp[32000];
    int cnt[N];//第i组有几个状态
    
    int main()
    {
        int n,m;
        RII(m,n);
        rep(i,1,n)//读入数据   储存到结构体里
        {
            RIII(a[i].v,a[i].c,a[i].id);
            a[i].c*=a[i].v;
            if(a[i].id)
            {
                num[ a[i].id ]++;
                fu[ a[i].id ][ num[ a[i].id ] ].v=a[i].v;
                fu[ a[i].id ][ num[ a[i].id ] ].c=a[i].c;
                fu[ a[i].id ][ num[ a[i].id ] ].id=a[i].id;
            }
        }
        
        rep(i,1,n)//开始对组的处理
        {
            if(num[i])//不是主件
            {
                CLR(dp,-1);//恰好背包初始化
                dp[0]=0;//恰好背包
                
                rep(j,1,num[i])
                for(int k=m-a[i].v;k>=fu[i][j].v;k--)
                    if(dp[k-fu[i][j].v ]!=-1)//注意恰好背包
                     dp[k]=max(dp[k],dp[k-fu[i][j].v ]+fu[i][j].c);
    
                rep(j,0,m-a[i].v)//储存每组的所有情况
                if(dp[j]!=-1)//恰好背包的判断  这种附件组合满足题意
                {
                    cnt[i]++;
                    v[i][cnt[i] ]=j+a[i].v;
                    c[i][cnt[i] ]=dp[j]+a[i].c;
                }
            }
            if(!a[i].id)//如果是主件   该组只有一个
            {
                cnt[i]++;
                v[i][cnt[i] ]=a[i].v;
                c[i][cnt[i] ]=a[i].c;
            }
        }
    
            CLR(dp,0);
            rep(i,1,n)
            for(int j=m;j>=0;j--)//第几组
            rep(k,1,cnt[i])//背包容量
            if(j>=v[i][k])//该组第几个状态
            dp[j]=max(dp[j],dp[j-v[i][k]]+c[i][k] );
    
            int maxx=0;
            rep(i,0,m)
            maxx=max(maxx,dp[i]);
            cout<<maxx;
        return 0;
    }
    View Code
     
  • 相关阅读:
    (剑指Offer)------二进制中1的个数
    LeetCode#58:最后一个单词的长度解析
    js 生成四个随机字母或数字+js获取当前日期
    ES6学习笔记----数组的扩展
    No component factory found for ListenerAddComponent. Did you add it to @NgModule.entryComponents?
    Can't bind to 'formGroup' since it isn't a known property of 'form'
    算法初相识---插入排序,冒泡排序,选择排序,以及分析算法
    Deno MongoDB 增删查改 接口
    Deno MySQL 增删查改接口
    Deno 几种常用的传参方式
  • 原文地址:https://www.cnblogs.com/bxd123/p/10505436.html
Copyright © 2011-2022 走看看