import java.io.*;
import java.util.*;
/**
* 组装最大可靠性设备
* 一个设备由N种元器件组成,每种类型元器件只需要一个,类型type编号范围0~N-1,每个元器件由可靠性属性reliability,
* 可靠性越高价格越贵,设备的可靠性由组成设备的所有器件中可靠性最低的器件决定。给定预算S,购买N种元器件
* 每种类型元器件都需要购买一个,在不超过预算的情况下,请给出能够组成的设备的最大可靠性
* 500 3 //500表示总预算,3表示元器件的种类
* 6 //元器件的总数
* 0 80 100 //0表示元器件类型编号,80表示元器件可靠性,100代表元器件的价格
* 输出描述:符合预算的设备的最大可靠性,如果预算无法买下N种器件,返回-1
*/
public class AssemblyOfMaximumReliabilityEquipment {
/*
500 3
6
0 90 200
0 80 100
1 70 210
1 50 50
2 50 100
2 60 150
----------------
60
++++++++++++++++
100 1
1
0 90 200
----------------
-1
*/
private static String line;
private static String[] strArr;
private static int[] nums;
/**
* 元器件目录,索引值代表元器件类型编号,列表代表该类型编号下的元器件,a[0]表示元器件可靠性,a[1]表示元器件价格
*/
private static List<List<int[]>> categories = new ArrayList<>();
/**
* 可靠性列表,排重之后升序排列
*/
private static Set<Integer> reliabilitySet = new TreeSet<>();
/**
* 可靠性列表
*/
private static List<Integer> reliabilityList = new ArrayList<>();
/**
* 总预算
*/
private static int budget;
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
line = in.readLine();
strArr = line.split(" ");
nums = Arrays.stream(strArr).mapToInt(Integer::parseInt).toArray();
budget = nums[0];
//元器件种类总数
int typeCount = nums[1];
line = in.readLine();
//元器件总数
int count = Integer.parseInt(line);
for (int i = 0; i < typeCount; i++) {
categories.add(new ArrayList<>());
}
for (int i = 0; i < count; i++) {
line = in.readLine();
strArr = line.split(" ");
nums = Arrays.stream(strArr).mapToInt(Integer::parseInt).toArray();
//元器件种类编号
int type = nums[0];
//元器件可靠性
int reliability = nums[1];
//元器件价格
int cost = nums[2];
categories.get(type).add(new int[]{reliability,cost});
reliabilitySet.add(reliability);
}
out.println(determineMaxReliability());
out.flush();
in.close();
out.close();
}
/**
* 在不超出预算的前提下,找到最大可靠性元器件
* @return 符合预算的设备的最大可靠性,如果预算无法买下N种器件,返回-1
*/
public static int determineMaxReliability() {
//所有可靠性排重后升序排序,加入列表
reliabilityList.addAll(reliabilitySet);
//确定可靠性的二分边界
int low = 0, high = reliabilityList.size() - 1;
int result = -1;
//开始二分循环
while (low <= high) {
int mid = low + (high - low) / 2;
//以中点可靠性为目标,预算不超,则认为当前位置可满足,取对应可靠性值即可
Integer target = reliabilityList.get(mid);
if (budgetIsEnough(target)) {
result = reliabilityList.get(mid);
low = mid + 1;
} else {
high = mid - 1;
}
}
return result;
}
/**
* 按照目标可靠性查找元器件
* @param targetReliability 目标可靠性
* @return 是否超预算
*/
private static boolean budgetIsEnough(int targetReliability) {
//总成本初始化为0
int totalCost = 0;
//遍历元器件目录
for (List<int[]> category : categories) {
//逐个型号查找,在目标可靠性下的最低价格
int minCostForTarget = findClosestComponent(category, targetReliability);
//单个超预算,则返回假
if (minCostForTarget == -1) {
return false;
}
//单个不超预算,则总成本加入当前元器件价格
totalCost += minCostForTarget;
}
//判断总成本是否超预算
return totalCost <= budget;
}
/**
* 找到最接近目标可靠性的元器件
* @param category 元器件列表
* @param targetReliability 目标可靠性
* @return 元器件价格
*/
private static int findClosestComponent(List<int[]> category, int targetReliability) {
//对目录中的元器件按可靠性升序排序
category.sort((a,b) -> a[0] - b[0]);
//确立二分边界
int low = 0, high = category.size() - 1;
//循环二分
while (low <= high) {
int mid = low + (high - low) / 2;
//获得当前可靠性
int currentReliability = category.get(mid)[0];
//找到最接近目标的可靠性值,记录对应坐标
if (currentReliability < targetReliability) {
low = mid + 1;
} else {
high = mid - 1;
}
}
//最终坐标值不越界且可靠性值大于等于目标值,则返回其成本,否则返回-1
boolean isValid = low < category.size() && category.get(low)[0] >= targetReliability;
if(isValid){
return category.get(low)[1];
}else {
return -1;
}
}
}
【二分法】组装最大可靠性设备
最新推荐文章于 2025-01-28 13:06:57 发布