Java 购物单

该博客讨论了一个使用动态规划解决的购物优化问题。王强收到年终奖并希望在不超过预算的情况下,最大化购买物品价格与重要度的乘积。物品分为主件和附件,附件依赖于主件。通过建立二维数组dum进行动态规划计算,找到最佳购物组合,最终输出最大总和。
摘要由CSDN通过智能技术生成

题目描述

王强今天很开心,公司发给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 )。
示例1

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

输出
2200

代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

pulic class Main{
	public static class Good {

        public int v;//价格
        public int p;//重要度
        public int q;//是否是附件

        public int a1 = 0;//附件1 id
        public int a2 = 0;//附件2 id

        public Good() {
        }

        public Good(int v, int p, int q) {
            this.v = v;
            this.p = p;
            this.q = q;
        }

        public void setA1(int a1) {
            this.a1 = a1;
        }

        public void setA2(int a2) {
            this.a2 = a2;
        }
    }

	public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String mn = scanner.nextLine();
        String[] arr1 = mn.split(" ");
        //钱,除去10方便计算,下面的每件物品的价格也都会除去10,最终输出结果会乘10
        int n = Integer.valueOf(arr1[0]) / 10;
        int m = Integer.valueOf(arr1[1]);//数量
        Good[] gs = new Good[m + 1];
        for (int i = 1; i <= m; i++) {
            String vpq = scanner.nextLine();
            String[] vpqArr = vpq.split(" ");
            int v = Integer.valueOf(vpqArr[0]) / 10;//价格
            int p = Integer.valueOf(vpqArr[1]);//重要度
            int q = Integer.valueOf(vpqArr[2]);//是否是主件
            //创建货物
            gs[i] = new Good(v, p, q);
            if (q > 0) {
                //如果是附件,则将其在数组中的位置保存到主件中
                if (gs[q].a1 == 0) {
                    //附件1
                    gs[q].setA1(i);
                } else {
                    //附件2
                    gs[q].setA2(i);
                }
            }
        }
        //开始动态规划
        int[][] dum = new int[m + 1][n + 1];
        //按物品循环
        for (int i = 1; i <= m; i++) {
            //计算每件商品的 v*p,如果有附件则需要加上附键的 v*p
            //三种情况,计算价格乘重要度的分数
            int v0 = 0, v1 = 0, v2 = 0, v3 = 0, temp0 = 0, temp1 = 0, temp2 = 0, temp3 = 0;
            // 主体
            v0 = gs[i].v;
            temp0 = v0 * gs[i].p;
            // 主+次1
            if (gs[i].a1 > 0) {
                v1 = v0 + gs[gs[i].a1].v;
                temp1 = temp0 + gs[gs[i].a1].v * gs[gs[i].a1].p;
            }
            // 主+次2
            if (gs[i].a2 > 0) {
                v2 = v0 + gs[gs[i].a2].v;
                temp2 = temp0 + gs[gs[i].a2].v * gs[gs[i].a2].p;
            }
            // 主+次12
            if (gs[i].a1 > 0 && gs[i].a2 > 0) {
                v3 = v1 + v2 - v0;
                temp3 = temp1 + temp2 - temp0;
            }
            //按目前所有金额循环
            for (int j = 1; j <= n; j++) {
                //附件循环时可忽略,因为只是复制主件的数据
                if (gs[i].q > 0) {
                    dum[i][j] = dum[i - 1][j];
                } else {
                    //下方按上面四种情况循环
                    dum[i][j] = dum[i - 1][j];
                    //当物品金额足够买当前组合时,当前数量物品的计分区间就会重合,然后叠加分数
                    //当前主件和其他主件组合之间计分
                    if (j >= v0 && v0 != 0) {
                        dum[i][j] = Math.max(dum[i][j], dum[i - 1][j - v0] + temp0);
                    }
                    //当前主件和附件1与其他主件组合的计分
                    if (j >= v1 && v1 != 0) {
                        dum[i][j] = Math.max(dum[i][j], dum[i - 1][j - v1] + temp1);
                    }
                    //当前主件和附件2与其他主件组合的计分
                    if (j >= v2 && v2 != 0) {
                        dum[i][j] = Math.max(dum[i][j], dum[i - 1][j - v2] + temp2);
                    }
                    //当前主件和附件1加附件2与其他主件组合的计分
                    if (j >= v3 && v3 != 0) {
                        dum[i][j] = Math.max(dum[i][j], dum[i - 1][j - v3] + temp3);
                    }
                }

            }
        }
        //最终最佳结果会累加在二次数列的右下角,也就是dum[m][n],乘上之前除去的10,输出结果
        System.out.println(dum[m][n] * 10);
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值