最小代价问题 | 豆包MarsCode AI刷题

129 阅读4分钟

一开始是用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);
    }
}

代码解释

  1. 数据结构

    • Map<Integer, PriorityQueue<Integer>> statistics:这个 Map 用于存储每个元素值及其对应的代价列表。PriorityQueue 用于存储每个元素值的代价,并按从小到大的顺序排列。
  2. 初始化

    • 遍历数组 a,将每个元素值及其对应的代价加入到 statistics 中。如果某个元素值已经存在,则将其代价加入到对应的 PriorityQueue 中。
  3. 处理重复元素

    • 使用 isRepeat 方法检查是否存在重复元素。如果存在,则需要进行处理。
    • 在 while 循环中,遍历 statistics,找到重复元素中代价最小的那个,并将其从 PriorityQueue 中移除。
    • 将该元素值加 1,并将新的元素值及其代价加入到 statistics 中。
    • 累加代价到 res 中。
  4. 终止条件

    • 当 isRepeat 返回 false 时,表示所有元素都各不相同,循环结束。

代码逻辑

  1. 初始化 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 中。

  2. 检查重复元素

    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,则表示存在重复元素。

  3. 处理重复元素

    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 中。