一、题目描述
在一款虚拟游戏中生活,你必须进行投资以增强在虚拟游戏中的资产以免被淘汰出局。
现有一家Bank,它提供有若干理财产品 m 个,风险及投资回报不同,你有 N(元)进行投资,能接收的总风险值为X。
你要在可接受范围内选择最优的投资方式获得最大回报。
备注:
- 在虚拟游戏中,每项投资风险值相加为总风险值;
- 在虚拟游戏中,最多只能投资2个理财产品;
- 在虚拟游戏中,最小单位为整数,不能拆分为小数;
- 投资额*回报率=投资回报
二、输入输出描述
输入描述
- 第一行:3 个整数,依次为产品数
m(1≤m≤20)、总投资额N(1≤N≤10000)、可接受总风险X(1≤X≤200); - 第二行:
m个整数,为每个产品的投资回报率(1≤回报率≤60); - 第三行:
m个整数,为每个产品的风险值(1≤风险值≤100); - 第四行:
m个整数,为每个产品的最大投资额度(1≤最大额度≤10000)。
输出描述
m个整数,为每个产品的投资额(未投资则为 0),按产品顺序输出。
三、示例
|----|---------------------------------------------------|
| 输入 | 5 100 10 10 20 30 40 50 3 4 5 6 10 20 30 20 40 30 |
| 输出 | 0 30 0 40 0 |
| 说明 | 投资第二项30个单位,第四项40个单位,总的投资风险为两项相加为4+6=10 |
四、解题思路
- 核心思想
在 "仅考虑单项目 / 双项目投资" 的简化场景下,通过枚举所有合法投资组合(单 / 双项目),按 "回报率优先级分配投资额",筛选出 "风险达标且回报最大" 的方案 ------ 核心是 "简化问题维度(仅单 / 双项目)+ 贪心分配投资额 + 枚举找最优"。
- 问题本质分析
- 表层问题:多约束下的投资组合优化(约束:总投资≤N、总风险≤X、单项目投资≤最大可投;目标:回报最大);
- 深层简化:代码未考虑 3 个及以上项目的组合,仅聚焦单 / 双项目(降低问题复杂度,适合小规模项目场景);
- 核心矛盾:"风险约束" 与 "回报最大化" 的平衡,通过 "优先投高回报率项目" 的贪心策略解决投资额分配问题。
- 核心逻辑
- 范围限定:仅枚举单项目、双项目组合(避免高维组合的复杂计算);
- 风险筛选:仅处理 "单项目风险≤X" 或 "双项目总风险≤X" 的组合(满足核心约束);
- 贪心分配:对合法组合,按回报率高低优先分配投资额(最大化单单位投资的回报);
- 最优更新:遍历所有合法组合,记录回报最大的投资方案。
-
步骤拆解
-
输入解析与初始化:
- 读取项目数、总投资、风险容忍度,以及每个项目的回报率、风险、最大可投;
- 初始化最大回报
maxReturn=0,最佳投资方案数组bestInvestments(全 0)。
-
单项目投资枚举:
- 遍历每个项目,筛选 "风险≤X" 的项目;
- 计算该项目的最大可投资额(总投资与项目最大可投的最小值);
- 计算回报,若大于当前最大回报,更新最大回报和投资方案。
-
双项目组合投资枚举:
- 遍历所有
i<j的项目组合,筛选 "总风险≤X" 的组合; - 按回报率优先级分配投资额(高回报率项目优先投满,剩余资金投低回报率项目);
- 计算组合回报,若大于当前最大回报,更新最大回报和投资方案。
- 遍历所有
-
结果输出:
- 将最佳投资方案数组按空格拼接,输出最终投资额分配。
五、代码实现
java
import java.util.Arrays;
import java.util.Scanner;
import java.util.StringJoiner;
public class Main {
public static void main(String[] args) {
// 创建Scanner对象用于获取用户输入
Scanner sc = new Scanner(System.in);
// 读取一行输入并将其分割为字符串数组,然后转换为整数数组
int[] tmp = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
// 获取投资项目数量m、总投资额N和风险容忍度X
int m = tmp[0];
int N = tmp[1];
int X = tmp[2];
// 读取每个项目的预期回报率
int[] returns = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
// 读取每个项目的风险值
int[] risks = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
// 读取每个项目的最大投资额
int[] maxInvestments = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
// 初始化最大回报为0
int maxReturn = 0;
// 初始化最大回报对应的投资方案数组
int[] bestInvestments = new int[m];
// 遍历所有项目
for (int i = 0; i < m; i++) {
// 如果单个项目的风险在容忍度范围内
if (risks[i] <= X) {
// 计算对项目i的投资额,不超过总投资额N和项目i的最大投资额
int investmentForI = Math.min(N, maxInvestments[i]);
// 计算当前回报
int currentReturn = investmentForI * returns[i];
// 如果当前回报大于已知的最大回报
if (currentReturn > maxReturn) {
// 更新最大回报
maxReturn = currentReturn;
// 重置最佳投资方案数组,并为项目i分配投资额
bestInvestments = new int[m];
bestInvestments[i] = investmentForI;
}
}
// 遍历项目i之后的项目,寻找两个项目的组合投资方案
for (int j = i + 1; j < m; j++) {
// 如果两个项目的风险总和在容忍度范围内
if (risks[i] + risks[j] <= X) {
int investmentForI, investmentForJ;
// 根据预期回报率决定投资额分配
if (returns[i] > returns[j]) {
// 如果项目i的回报率高于项目j,优先投资项目i
investmentForI = Math.min(N, maxInvestments[i]);
investmentForJ = Math.min(N - investmentForI, maxInvestments[j]);
} else {
// 如果项目j的回报率高于项目i,优先投资项目j
investmentForJ = Math.min(N, maxInvestments[j]);
investmentForI = Math.min(N - investmentForJ, maxInvestments[i]);
}
// 计算当前两个项目组合的回报
int currentReturn = investmentForI * returns[i] + investmentForJ * returns[j];
// 如果当前回报大于已知的最大回报
if (currentReturn > maxReturn) {
// 更新最大回报
maxReturn = currentReturn;
// 重置最佳投资方案数组,并为两个项目分配投资额
bestInvestments = new int[m];
bestInvestments[i] = investmentForI;
bestInvestments[j] = investmentForJ;
}
}
}
}
// 创建StringJoiner对象,用于构建输出格式
StringJoiner sj = new StringJoiner(" ");
// 遍历最佳投资方案数组,将投资额添加到StringJoiner中
for (int investment : bestInvestments) {
sj.add(String.valueOf(investment));
}
// 输出最佳投资方案
System.out.println(sj.toString());
// 关闭Scanner对象
sc.close();
}
}