手机壳销售盈利最大化
问题背景
为了实现盈利最大化,一个手机壳销售商店需要制定合理的库存销售策略。商店里有多种不同型号的手机壳,每种型号都有一定的库存量和对应的总售价。现在,需要根据市场的最大需求量来决定每种型号手机壳的销售数量,以获得最大的总销售收益。
核心定义与公式
-
单价(Unit Price):
这是做出最优决策的关键指标。每种型号手机壳的单价计算方式为:
单价 = 总售价 / 库存数量
即 unit_price[i] = price[i] / inventory[i]
-
销售收益:
- 单品收益:
销售数量 * 单价 - 总销售收益: 所有售出型号的手机壳收益之和。
- 单品收益:
优化目标
在满足市场最大需求量 demand 这个总销售数量上限的前提下,决定每种型号手机壳的销售数量,使得总销售收益达到最大。
决策策略:
这是一个典型的贪心问题。为了实现总收益最大化,我们应该优先销售单价最高的手机壳。
任务要求
给定手机壳的种类数量、市场最大需求量、每种型号的库存量和总售价,请计算能够获得的最大总销售收益。
输入格式
-
第一行: 两个正整数
M和N。M: 手机壳的种类数量。N: 市场的最大需求量(单位:千部)。
-
第二行:
M个数字,代表每种型号手机壳的库存数量inventory(单位:千部),数字之间用空格隔开。 -
第三行:
M个数字,代表每种型号手机壳的总售价price(单位:万元),顺序与第二行的库存数量一一对应。
输出格式
- 一个浮点数,表示可以获得的最大总销售收益(单位:万元)。
- 系统对结果进行浮点数判断,允许
0.01的误差。
限制与要求
-
时间限制: C/C++
1000ms, 其他语言2000ms -
内存限制: C/C++
256MB, 其他语言512MB -
数值范围:
1 <= M <= 10001 <= N <= 5000.0 < inventory[i] <= 1000.00.0 < price[i] <= 10000.0
样例说明
样例输入
3 20
18.0 15.0 10.0
75.0 72.0 45.0
样例输出
94.50
解释
-
数据整理与分析:
- 市场最大需求量
demand = 20(千部)。 - 我们有三种型号的手机壳,需要计算各自的单价来决定销售优先级。
型号 库存 (千部) 总售价 (万元) 单价 (万元/千部) 1 18.0 75.0 75.0/18.0approx4.17 2 15.0 72.0 72.0/15.0=4.80 3 10.0 45.0 45.0/10.0=4.50 - 市场最大需求量
-
应用贪心策略:
-
第一步: 比较单价,型号2的单价最高(4.80)。优先销售型号2。
- 型号2的库存为
15.0,小于市场需求20。我们可以全部卖出。 - 销售:
15.0千部型号2。 - 获得收益:
72.0万元 (因为是全卖光了,直接用总售价)。 - 剩余需求:
20 - 15.0 = 5.0千部。
- 型号2的库存为
-
第二步: 剩下的型号中,型号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万元。
-
最终结果: 最大收益为
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);
}
}