一开始是用Map<Integer, List<Integer>>来做统计,然后用List记录每个数的索引对应的代价,list的大小就是数的数量,大于2的就是重复。然后再循环里面判断map里面的list的size大小,如果大于1就说明重复,然后在循环里面对map直接进行操作选出list最小的代价然后对最小代价的数加1,然后更新map(删除和增加)
然后下面就是代码的初始版本:
public class 最小代价问题 {
public static int solution(int n, int[] a, int[] b) {
// write code here
//用map做统计
//用List记录每个数的索引对应的代价,list的大小就是数的数量,大于2的就是重复
Map<Integer, List<Integer>> statistics=new HashMap<>();
for (int i = 0; i < a.length; i++) {
if(statistics.containsKey(a[i])){
statistics.get(a[i]).add(b[i]);
}else{
List<Integer> list=new ArrayList<>();
list.add(b[i]);
statistics.put(a[i],list);
}
}
int res=0,intrenum;
AtomicInteger renum = new AtomicInteger();
final int[] mincost = { 1000000000 };
//lambda表达式或匿名类中使用的变量必须是final或有效final(即在初始化后没有被修改)。这是为了确保lambda表达式在多线程环境中使用时的安全性和一致性。
while(isRepeat(statistics)){
//得到重复的数和最小代价
statistics.forEach((k,v)->{
if(v.size()>1){
v.forEach(i->{
if(i< mincost[0]){
mincost[0] =i;
renum.set(k);
}
});
}
});
intrenum=renum.get();
//对最小代价的数加1,然后更新map(删除和增加)
//statistics.get(intrenum).forEach(System.out::println);
statistics.get(intrenum).remove(Integer.valueOf(mincost[0]));
intrenum++;
if(statistics.containsKey(intrenum)){
statistics.get(intrenum).add(mincost[0]);
}else{
List<Integer> ls=new ArrayList<>();
ls.add(mincost[0]);
statistics.put(intrenum,ls);
}
res+=mincost[0];
}
return res;
}
//判断是否还有重复的数
public static boolean isRepeat(Map<Integer,List<Integer>> statistics){
for(List<Integer> list:statistics.values()){
if(list.size()>1){
return true;
}
}
return false;
}
public static void main(String[] args) {
System.out.println(solution(5, new int[]{1, 2, 3, 4, 5}, new int[]{1, 1, 1, 1, 1}) == 0);
System.out.println(solution(3, new int[]{1, 1, 2}, new int[]{4, 5, 3}) == 7);
}
}
毫无疑问直接超时了
后来使用优先队列(PriorityQueue)来存储代价最小的元素。使用一个数组来记录每个元素的代价总和,优化了一下:
import java.util.*;
public class 最小代价问题 {
public static int solution(int n, int[] a, int[] b) {
Map<Integer, PriorityQueue<Integer>> statistics = new HashMap<>();
for (int i = 0; i < a.length; i++) {
statistics.computeIfAbsent(a[i], k -> new PriorityQueue<>()).add(b[i]);
}
int res = 0;
while (isRepeat(statistics)) {
int minCost = Integer.MAX_VALUE;
int minKey = -1;
for (Map.Entry<Integer, PriorityQueue<Integer>> entry : statistics.entrySet()) {
if (entry.getValue().size() > 1) {
int cost = entry.getValue().peek();
if (cost < minCost) {
minCost = cost;
minKey = entry.getKey();
}
}
}
if (minKey != -1) {
statistics.get(minKey).poll();
int newKey = minKey + 1;
statistics.computeIfAbsent(newKey, k -> new PriorityQueue<>()).add(minCost);
res += minCost;
}
}
return res;
}
public static boolean isRepeat(Map<Integer, PriorityQueue<Integer>> statistics) {
for (PriorityQueue<Integer> queue : statistics.values()) {
if (queue.size() > 1) {
return true;
}
}
return false;
}
public static void main(String[] args) {
System.out.println(solution(5, new int[]{1, 2, 3, 4, 5}, new int[]{1, 1, 1, 1, 1}) == 0);
System.out.println(solution(3, new int[]{1, 1, 2}, new int[]{4, 5, 3}) == 7);
}
}
代码解释
-
数据结构:
Map<Integer, PriorityQueue<Integer>> statistics:这个Map用于存储每个元素值及其对应的代价列表。PriorityQueue用于存储每个元素值的代价,并按从小到大的顺序排列。
-
初始化:
- 遍历数组
a,将每个元素值及其对应的代价加入到statistics中。如果某个元素值已经存在,则将其代价加入到对应的PriorityQueue中。
- 遍历数组
-
处理重复元素:
- 使用
isRepeat方法检查是否存在重复元素。如果存在,则需要进行处理。 - 在
while循环中,遍历statistics,找到重复元素中代价最小的那个,并将其从PriorityQueue中移除。 - 将该元素值加 1,并将新的元素值及其代价加入到
statistics中。 - 累加代价到
res中。
- 使用
-
终止条件:
- 当
isRepeat返回false时,表示所有元素都各不相同,循环结束。
- 当
代码逻辑
-
初始化
statistics:Map<Integer, PriorityQueue<Integer>> statistics = new HashMap<>(); for (int i = 0; i < a.length; i++) { statistics.computeIfAbsent(a[i], k -> new PriorityQueue<>()).add(b[i]); }这段代码将数组
a中的每个元素及其对应的代价加入到statistics中。如果某个元素值已经存在,则将其代价加入到对应的PriorityQueue中。 -
检查重复元素:
public static boolean isRepeat(Map<Integer, PriorityQueue<Integer>> statistics) { for (PriorityQueue<Integer> queue : statistics.values()) { if (queue.size() > 1) { return true; } } return false; }这个方法遍历
statistics中的每个PriorityQueue,如果某个PriorityQueue的大小大于 1,则表示存在重复元素。 -
处理重复元素:
while (isRepeat(statistics)) { int minCost = Integer.MAX_VALUE; int minKey = -1; for (Map.Entry<Integer, PriorityQueue<Integer>> entry : statistics.entrySet()) { if (entry.getValue().size() > 1) { int cost = entry.getValue().peek(); if (cost < minCost) { minCost = cost; minKey = entry.getKey(); } } } if (minKey != -1) { statistics.get(minKey).poll(); int newKey = minKey + 1; statistics.computeIfAbsent(newKey, k -> new PriorityQueue<>()).add(minCost); res += minCost; } }这段代码在
while循环中不断检查是否存在重复元素,并找到重复元素中代价最小的那个进行处理。具体步骤如下:- 遍历
statistics,找到重复元素中代价最小的那个。 - 将该元素值加 1,并将新的元素值及其代价加入到
statistics中。 - 累加代价到
res中。
- 遍历