手机壳销售盈利最大化

87 阅读5分钟

手机壳销售盈利最大化

问题背景

为了实现盈利最大化,一个手机壳销售商店需要制定合理的库存销售策略。商店里有多种不同型号的手机壳,每种型号都有一定的库存量和对应的总售价。现在,需要根据市场的最大需求量来决定每种型号手机壳的销售数量,以获得最大的总销售收益。

核心定义与公式

  1. 单价(Unit Price):

    这是做出最优决策的关键指标。每种型号手机壳的单价计算方式为:

    单价 = 总售价 / 库存数量

    即 unit_price[i] = price[i] / inventory[i]

  2. 销售收益:

    • 单品收益: 销售数量 * 单价
    • 总销售收益: 所有售出型号的手机壳收益之和。

优化目标

在满足市场最大需求量 demand 这个总销售数量上限的前提下,决定每种型号手机壳的销售数量,使得总销售收益达到最大。

决策策略:

这是一个典型的贪心问题。为了实现总收益最大化,我们应该优先销售单价最高的手机壳。

任务要求

给定手机壳的种类数量、市场最大需求量、每种型号的库存量和总售价,请计算能够获得的最大总销售收益。


输入格式

  • 第一行: 两个正整数 MN

    • M: 手机壳的种类数量。
    • N: 市场的最大需求量(单位:千部)。
  • 第二行: M 个数字,代表每种型号手机壳的库存数量 inventory(单位:千部),数字之间用空格隔开。

  • 第三行: M 个数字,代表每种型号手机壳的总售价 price(单位:万元),顺序与第二行的库存数量一一对应。


输出格式

  • 一个浮点数,表示可以获得的最大总销售收益(单位:万元)。
  • 系统对结果进行浮点数判断,允许 0.01 的误差。

限制与要求

  • 时间限制: C/C++ 1000ms, 其他语言 2000ms

  • 内存限制: C/C++ 256MB, 其他语言 512MB

  • 数值范围:

    • 1 <= M <= 1000
    • 1 <= N <= 500
    • 0.0 < inventory[i] <= 1000.0
    • 0.0 < price[i] <= 10000.0

样例说明

样例输入

3 20
18.0 15.0 10.0
75.0 72.0 45.0

样例输出

94.50

解释

  1. 数据整理与分析:

    • 市场最大需求量 demand = 20 (千部)。
    • 我们有三种型号的手机壳,需要计算各自的单价来决定销售优先级。
    型号库存 (千部)总售价 (万元)单价 (万元/千部)
    118.075.075.0/18.0approx4.17
    215.072.072.0/15.0=4.80
    310.045.045.0/10.0=4.50
  2. 应用贪心策略:

    • 第一步: 比较单价,型号2的单价最高(4.80)。优先销售型号2。

      • 型号2的库存为 15.0,小于市场需求 20。我们可以全部卖出。
      • 销售: 15.0 千部型号2。
      • 获得收益: 72.0 万元 (因为是全卖光了,直接用总售价)。
      • 剩余需求: 20 - 15.0 = 5.0 千部。
    • 第二步: 剩下的型号中,型号3的单价较高(4.50)。接着销售型号3。

      • 型号3的库存为 10.0,大于剩余需求 5.0。我们只需要卖出 5.0 千部即可满足市场总需求。
      • 销售: 5.0 千部型号3。
      • 获得收益: 销售数量 * 单价 = 5.0 * 4.50 = 22.5 万元。
      • 剩余需求: 5.0 - 5.0 = 0
  3. 计算总收益:

    • 市场需求已满足,停止销售。
    • 总收益 = (来自型号2的收益) + (来自型号3的收益) = 72.0 + 22.5 = 94.5 万元。
  4. 最终结果: 最大收益为 94.50

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

/**
 * 辅助类,用于封装每种型号手机壳的属性。
 * 包含了库存、总售价以及计算出的单位收益(单价)。
 * 实现 Comparable 接口,以便能根据单位收益对不同型号的手机壳进行排序。
 */
class PhoneCaseType implements Comparable<PhoneCaseType> {
    double inventory;  // 库存数量(单位:千部)
    double price;      // 该型号所有库存的总售价(单位:万元)
    double unitProfit; // 单位收益,即每千部手机壳的售价

    public PhoneCaseType(double inventory, double price) {
        this.inventory = inventory;
        this.price = price;
        // 计算单位收益 = 总售价 / 库存数量
        // 处理库存为0的边界情况,避免除以零的错误
        this.unitProfit = (inventory > 0) ? price / inventory : 0;
    }

    /**
     * 实现 compareTo 方法,用于定义排序规则。
     * 我们希望按单位收益 (unitProfit) 进行降序排序,
     * 以便优先销售利润最高的手机壳。
     * @param other 另一个要比较的 PhoneCaseType 对象
     * @return 负数、零或正数,分别表示当前对象小于、等于或大于指定对象。
     */
    @Override
    public int compareTo(PhoneCaseType other) {
        // Double.compare(d1, d2) 返回 d1 和 d2 的比较结果。
        // 为了实现降序,我们将参数位置反转。
        // 如果 other.unitProfit > this.unitProfit,返回正数,this会排在后面。
        return Double.compare(other.unitProfit, this.unitProfit);
    }
}

/**
 * 主类,用于解决“手机壳库存管理”问题。
 * 采用 ACM 模式处理输入和输出。
 */
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // --- 1. 读取输入 ---
        int m = scanner.nextInt();       // M: 手机壳种类的个数
        double demand = scanner.nextDouble(); // N: 市场最大需求量 (千部)

        // 读取库存和总售价数组
        double[] inventories = new double[m];
        double[] prices = new double[m];
        for (int i = 0; i < m; i++) {
            inventories[i] = scanner.nextDouble();
        }
        for (int i = 0; i < m; i++) {
            prices[i] = scanner.nextDouble();
        }
        scanner.close(); // 输入读取完毕

        // --- 2. 准备数据 ---
        // 创建一个 PhoneCaseType 对象的列表,将每种手机壳的属性封装在一起。
        List<PhoneCaseType> caseTypes = new ArrayList<>();
        for (int i = 0; i < m; i++) {
            // 只处理有库存的手机壳
            if (inventories[i] > 0) {
                caseTypes.add(new PhoneCaseType(inventories[i], prices[i]));
            }
        }

        // --- 3. 贪心策略:按单位收益排序 ---
        // 这是解决问题的核心。为了获得最大总收益,我们应该优先卖出单位收益最高的手机壳。
        // Collections.sort 会使用 PhoneCaseType 类中定义的 compareTo 方法进行排序。
        Collections.sort(caseTypes);

        // --- 4. 模拟销售过程,计算最大收益 ---
        double totalProfit = 0.0;     // 初始化总收益
        double remainingDemand = demand; // 初始化剩余市场需求量

        // 遍历按单位收益降序排列后的手机壳列表
        for (PhoneCaseType caseType : caseTypes) {
            // 如果市场需求已经满足,则停止销售
            if (remainingDemand <= 0) {
                break;
            }

            // 确定当前型号可以销售的数量
            // 数量取决于该型号的库存量和当前剩余的市场需求量,取两者中的较小值。
            double quantityToSell = Math.min(caseType.inventory, remainingDemand);

            // 计算本次销售带来的收益,并累加到总收益中
            // 收益 = 销售数量 * 单位收益
            totalProfit += quantityToSell * caseType.unitProfit;

            // 更新剩余的市场需求量
            remainingDemand -= quantityToSell;
        }

        // --- 5. 输出结果 ---
        // 按样例格式要求,输出保留两位小数的浮点数
        System.out.printf("%.2f%n", totalProfit);
    }
}