华为OD机试 - 物流仓储多维度成本利润综合查询系统 - 二维数组(Java 新系统 100分)

在这里插入图片描述

华为OD机试 新系统 题库疯狂收录中,刷题点这里

专栏导读

本专栏收录于《华为OD机试(JAVA)真题》

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。

一、题目描述

某物流企业在全国多个仓库开展仓储业务,需要对不同仓库、不同时段的入库成本、出库收入和库存损耗进行综合统计分析,用于生成
日报、周报和月报。

字段说明及取值范围如下:

字段类型取值范围说明
warehouseIdint[0, 99]仓库编号
numOfWarehouseint[0, 99]仓库数量
startDayint[0, 99]统计开始天
endDayint[0, 99]统计结束天,startDay ≤ endDay
inCostint[0, 1000]入库成本(千元)
outRevenueint[0, 2000]出库收入(千元)
lossint[0, 100]库存损耗(千元),0表示无损耗

二、输入描述

warehouses: int[] 扁平化数组 [w0d0in, w0d0out, w0d0loss, w0d1in, w0d1out, w0d1loss, …],所有仓库数据都存在该数据中,先存储仓库0的数据,再存储仓库1的数据,所有仓库数据个数均相同

queries: int[][] [[warehouseId, startDay, endDay], …]

numOfWarehouse: int 仓库个数

输入保证最多100个仓库,每个仓库最多100天数据,每天数据连续,中间无空缺

三、输出描述

返回 int[][] 每个查询结果:[累计净利润, 累计损耗, 风控标记]
业务计算规则:
净利润 = 累计出库收入 - 累计入库成本
累计损耗 = Σ每日损耗
损耗率 = 累计损耗 / (累计入库成本 + 累计出库收入) × 100%,保留2位小数
风控规则:
当损耗率 > 5% 时,触发风控标记(返回1)
当损耗率 ≤ 5% 时,不触发风控(返回0)

四、测试用例

测试用例1:

1、输入

3,8,1,5,12,0,2,6,0,4,10,1
0,0,2
1

2、输出

16,1,0

3、说明

查询仓库0的第0~2天:

累计入库成本 = 3 + 5 + 2 = 10
累计出库收入 = 8 + 12 + 6 = 26

净利润 = 26 - 10 = 16

累计损耗 = 1

损耗率 = 1 / 36 × 100% ≈ 2.78%

不超过5%,因此风控标记为0。

测试用例2:

1、输入

10,20,5,10,20,5,10,20,5,5,15,2,5,15,2,5,15,2
0,0,2 1,0,1
2

2、输出

30,15,1 20,4,1

3、说明

每个仓库有3天数据。

第一个查询:

仓库0,第0~2天

累计入库 = 30
累计收入 = 60
净利润 = 30
累计损耗 = 15

损耗率 = 15 / 90 × 100% = 16.67%

触发风控。

第二个查询:

仓库1,第0~1天

累计入库 = 10
累计收入 = 30
净利润 = 20
累计损耗 = 4

损耗率 = 4 / 40 × 100% = 10.00%

同样触发风控。

五、解题思路

本题采用 二维前缀和 来处理每个仓库的区间统计查询。

由于所有仓库每天都有 入库成本、出库收入、库存损耗 3 项数据,并且每天连续,因此可以分别为每个仓库维护三组前缀和数组:

inPrefix:累计入库成本

outPrefix:累计出库收入

lossPrefix:累计库存损耗

预处理完成后,对于查询 [warehouseId, startDay, endDay],可以通过:区间累计值 = prefix[endDay + 1] - prefix[startDay],直接得到指定时间段的累计入库成本、累计出库收入和累计损耗,然后计算:累计净利润 = 累计出库收入 - 累计入库成本

损耗率 = 累计损耗 / (累计入库成本 + 累计出库收入) × 100%

根据损耗率是否大于 5% 得到风控标记。

六、Java算法源码

public class OdTest {

    // 将一段由逗号、空格混合分隔的整数文本转换为 int 数组
    private static int[] parseNumbers(String text) {
        text = text.trim();
        if (text.isEmpty()) {
            return new int[0];
        }

        String[] parts = text.split("[,\\s]+");
        int[] nums = new int[parts.length];

        for (int i = 0; i < parts.length; i++) {
            nums[i] = Integer.parseInt(parts[i]);
        }

        return nums;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 第一部分:所有仓库的扁平化数据
        int[] warehouses = parseNumbers(scanner.nextLine());

        int[] queryNums = parseNumbers(scanner.nextLine());

        // 最后一行:仓库数量
        int numOfWarehouse = Integer.parseInt(scanner.nextLine());

        if (numOfWarehouse <= 0 || warehouses.length == 0) {
            System.out.println();
            return;
        }

        /*
         * 每个仓库每天固定有:
         * inCost、outRevenue、loss
         * 共 3 个整数。
         *
         * 所有仓库天数相同,因此可以反推出每个仓库有多少天数据。
         */
        int days = warehouses.length / (numOfWarehouse * 3);

        /*
         * prefix[w][d] 表示:
         * 仓库 w 前 d 天的累计值。
         *
         * 多设置一个位置 0,可以方便计算从第 0 天开始的区间。
         */
        int[][] inPrefix = new int[numOfWarehouse][days + 1];

        int[][] outPrefix = new int[numOfWarehouse][days + 1];

        int[][] lossPrefix = new int[numOfWarehouse][days + 1];

        // 构造三个前缀和数组
        for (int w = 0; w < numOfWarehouse; w++) {
            for (int d = 0; d < days; d++) {

                /*
                 * warehouses 按:
                 *
                 * 仓库0所有天、
                 * 仓库1所有天、
                 * ...
                 *
                 * 顺序排列。
                 *
                 * 每天占 3 个位置。
                 */
                int index = (w * days + d) * 3;

                inPrefix[w][d + 1] = inPrefix[w][d] + warehouses[index];

                outPrefix[w][d + 1] = outPrefix[w][d] + warehouses[index + 1];

                lossPrefix[w][d + 1] = lossPrefix[w][d] + warehouses[index + 2];
            }
        }

        StringBuilder ans = new StringBuilder();

        // 每三个数字组成一个查询
        for (int i = 0; i + 2 < queryNums.length; i += 3) {

            int warehouseId = queryNums[i];
            int startDay = queryNums[i + 1];
            int endDay = queryNums[i + 2];

            /*
             * 使用前缀和计算闭区间 [startDay, endDay]。
             * 只需要两次数组访问和一次减法。
             */
            int totalIn = inPrefix[warehouseId][endDay + 1] - inPrefix[warehouseId][startDay];

            int totalOut = outPrefix[warehouseId][endDay + 1] - outPrefix[warehouseId][startDay];

            int totalLoss = lossPrefix[warehouseId][endDay + 1] - lossPrefix[warehouseId][startDay];

            // 净利润 = 累计收入 - 累计成本
            int netProfit = totalOut - totalIn;

            // 损耗率的分母
            int totalAmount = totalIn + totalOut;

            int risk = 0;

            if (totalAmount != 0) {

                /*
                 * 损耗率:
                 *
                 * totalLoss / totalAmount * 100%
                 *
                 * 题目要求保留两位小数。
                 *
                 * 将百分数扩大 100 倍:
                 * 5.00% -> 500
                 * 5.01% -> 501
                 *
                 * 所以需要计算:
                 * round(totalLoss * 10000 / totalAmount)
                 *
                 * 这里全部使用整数完成四舍五入,
                 * 避免 double 在 5% 临界值附近出现误差。
                 */
                long numerator = (long) totalLoss * 10000;

                long roundedRate = (numerator * 2 + totalAmount) / (2L * totalAmount);

                // 保留两位小数以后严格大于 5.00% 才触发
                risk = roundedRate > 500 ? 1 : 0;
            }

            if (ans.length() > 0) {
                ans.append(' ');
            }

            ans.append(netProfit)
                    .append(',')
                    .append(totalLoss)
                    .append(',')
                    .append(risk);
        }

        System.out.println(ans);
    }
}

七、效果展示

1、输入

5,10,0,8,12,1,20,15,2,10,30,0,100,120,5,50,40,1,0,20,2,30,90,3
0,1,3 1,2,3
2

2、输出

19,3,0 80,5,0

3、说明

每个仓库4天。

第一个查询:仓库0,第1~3天

累计入库 = 8 + 20 + 10 = 38
累计收入 = 12 + 15 + 30 = 57
净利润 = 19

累计损耗 = 1 + 2 + 0 = 3
损耗率 = 3 / 95 × 100% ≈ 3.16%

输出:19,3,0

第二个查询:

仓库1,第2~3天

累计入库 = 0 + 30 = 30
累计收入 = 20 + 90 = 110
净利润 = 80

累计损耗 = 2 + 3 = 5
损耗率 = 5 / 140 × 100% ≈ 3.57%

输出:80,5,0

在这里插入图片描述


🏆下一篇:华为OD机试 - 简易内存池 - 逻辑分析(Java 新系统 200分)

🏆本专栏收录于《华为OD机试(JAVA)真题》

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值