动态规划-HJ16 购物单

描述

王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 附件
电脑 打印机,扫描仪
书柜 图书
书桌 台灯,文具
工作椅
如果要买归类为附件的物品,必须先买该附件所属的主件,且每件物品只能购买一次。
每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。
王强查到了每件物品的价格(都是 10 元的整数倍),而他只有 N 元的预算。除此之外,他给每件物品规定了一个重要度,用整数 1 ~ 5 表示。他希望在花费不超过 N 元的前提下,使自己的满意度达到最大。
满意度是指所购买的每件物品的价格与重要度的乘积的总和,请你帮助王强计算可获得的最大的满意度。

输入描述:

输入的第 1 行,为两个正整数N,m,用一个空格隔开:

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

 

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

 

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

 

 

输出描述:

 输出一个正整数,为张强可以获得的最大的满意度。

示例1

输入:
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
输出:
2200

示例2

输入:
50 5
20 3 5
20 3 5
10 3 0
10 2 0
10 1 0
输出:
130
说明:
由第1行可知总钱数N为50以及希望购买的物品个数m为5;
第2和第3行的q为5,说明它们都是编号为5的物品的附件;
第4~6行的q都为0,说明它们都是主件,它们的编号依次为3~5;
所以物品的价格与重要度乘积的总和的最大值为10*1+20*3+20*3=130  


解法:

具体思路就是构造物品类,然后对主件判断是否有附件,若有附件则依次添加,根据主件、附件1、附件2的组合有四种情况

    • 只有主件
    • 主件+附件1
    • 主件+附件2
    • 主件+附件1+附件2
      根据以上情况转化问题为经典的 01背包问题 ,接着就是套公式动态规划即可
package dayone.easy;

import java.util.Scanner;


class Goods {
    int v;
    int p;
    boolean main = false;

    int a1 = -1;  //定义附件1的编号
    int a2 = -1;  //定义附件2的编号
}

/***
 * 1000 5
 * 800 2 0
 * 400 5 1
 * 300 5 1
 * 400 3 0
 * 500 2 0
 */
class MtTest {
    public static int dw = 100;//加快运行

    public static void main(String[] args) {
        boolean flag = true;
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt(); //总钱数
        int n = sc.nextInt(); //购买个数

        //初始化goods
        Goods[] goods = new Goods[m];
        for (int i = 1; i <= n; i++) {
            goods[i] = new Goods();
        }

        //填充goods,并将附件编号加在对应的主件上(赋值到a1,a2)
        for (int i = 1; i <= n; i++) {
            int v = sc.nextInt();
            int p = sc.nextInt();
            int q = sc.nextInt();
            if (flag) {
                if (v % dw != 0) {
                    flag = false;
                    dw = 10;
                    for (int j = 1; j < i; j++)
                        goods[j].v = goods[j].v * 10;
                }
            }
            v = v / dw;
            goods[i].p = p;
            goods[i].v = v;
            if (q == 0) {
                goods[i].main = true;
            } else if (goods[q].a1 == -1) {
                goods[q].a1 = i;
            } else {
                goods[q].a2 = i;
            }
        }
        m/=dw;
        //利用动态规划01背包问题解决该问题
        int[][] dp = new int[n + 1][m + 1];
        //物品
        for (int i = 1; i <= n; i++) {
            boolean main = goods[i].main;

            int v1 = 0, v2 = 0, v3 = 0, tempDp = 0, tempDp1 = 0, tempDp2 = 0, tempDp3 = 0;
            int v = goods[i].v;
            int p = goods[i].p;
            int a1 = goods[i].a1;
            int a2 = goods[i].a2;

            //第一种情况:全部选择主件
            tempDp = p * v;
            //第二种情况:选择主件的同时选择a1和a2作为附件
            if (a1 != -1 && a2 != -1) {
                v1 = v + goods[a1].v + goods[a2].v;
                tempDp1 = tempDp + goods[a1].v * goods[a1].p + goods[a2].v * goods[a2].p;
            }
            //第三种情况:选择主件同时选择a2作为附件
            if (a2 != -1) {
                v2 = v + goods[a2].v;
                tempDp2 = tempDp + goods[a2].v * goods[a2].p;
            }
            //第四种情况:选择主件同时选择a1作为附件
            if (a1 != -1) {
                v3 = v + goods[a1].v;
                tempDp3 = tempDp + goods[a1].v * goods[a1].p;
            }
            //金额
            for (int j = 1; j <= m; j++) {
                dp[i][j] = dp[i - 1][j];
                if (!main) continue;
                if (v != 0 && j >= v) dp[i][j] = Math.max(dp[i][j], tempDp + dp[i - 1][j - v]);
                if (v1 != 0 && j >= v1) dp[i][j] = Math.max(dp[i][j], tempDp1 + dp[i - 1][j - v1]);
                if (v2 != 0 && j >= v2) dp[i][j] = Math.max(dp[i][j], tempDp2 + dp[i - 1][j - v2]);
                if (v3 != 0 && j >= v3) dp[i][j] = Math.max(dp[i][j], tempDp3 + dp[i - 1][j - v3]);
            }
        }
        System.out.println(dp[n][m]*dw);
    }
}

 



posted @ 2023-01-26 11:53  KeepSmiling_me  阅读(319)  评论(0)    收藏  举报