周赛331总结

简介: 思路:使用大顶堆存储所有的礼物,每次选择礼物数量最多的一堆,即堆顶元素,然后将剩余礼物数量放入堆中,反复执行操作k次,返回剩余的礼物数量

5-周赛331总结


都有思路,但是第三题第四题代码就差了一点点,还是时间不够+不大熟练


从数量最多的堆取走礼物【LC6348】


给你一个整数数组 gifts ,表示各堆礼物的数量。每一秒,你需要执行以下操作:


  • 选择礼物数量最多的那一堆。
  • 如果不止一堆都符合礼物数量最多,从中选择任一堆即可。
  • 选中的那一堆留下平方根数量的礼物(向下取整),取走其他的礼物。


返回在 k 秒后剩下的礼物数量*。*


  • 思路:使用大顶堆存储所有的礼物,每次选择礼物数量最多的一堆,即堆顶元素,然后将剩余礼物数量放入堆中,反复执行操作k次,返回剩余的礼物数量


  • 实现


class Solution {
    public long pickGifts(int[] gifts, int k) {
        PriorityQueue<Integer> pq = new PriorityQueue<>((o1,o2) -> (o2 - o1));
        long sum = 0L;
        for (int g : gifts){
            sum += g;
            pq.add(g);
        }
        for (int i = 0; i < k; i++){
            int max = pq.poll();
            if (max == 1) break;
            int s = (int)Math.sqrt(max);
            sum -= max - s;
            pq.add(s);
        }
        return sum;
    }
}


。复杂度


  • 时间复杂度:O ( n )
  • 空间复杂度:O ( 1 )


统计范围内的元音字符串数【LC6347】


给你一个下标从 0 开始的字符串数组 words 以及一个二维整数数组 queries 。


每个查询 queries[i] = [li, ri] 会要求我们统计在 words 中下标在 li 到 ri 范围内(包含 这两个值)并且以元音开头和结尾的字符串的数目。


返回一个整数数组,其中数组的第 i 个元素对应第 i 个查询的答案。


**注意:**元音字母是 'a'、'e'、'i'、'o' 和 'u' 。


  • 思路:需要查询words中某个区间的元音字符串的个数,因此使用前缀和数组sum[i+1]计算出下标范围为[0,i]的元音字符串个数,区间[l,r]内的元音字符串的个数即为sum[r+1]−sum[l]


  • 实现


class Solution {
    public int[] vowelStrings(String[] words, int[][] queries) {
        int n = words.length;
        int m = queries.length;
        int[] sum = new int[n + 1];
        int[] res = new int[m];
        Arrays.fill(res, 0);
        for (int i = 0; i < n; i++){
            if (isVowel(words[i])){
                sum[i + 1] = sum[i] + 1;
            }else{
                sum[i + 1] = sum[i];
            }
        }
        for (int i = 0; i < m; i++){
            int l = queries[i][0], r = queries[i][1];
            res[i] = sum[r + 1] - sum[l];
        }
        return res;
    }
    public boolean isVowel(String word){
        char c = word.charAt(0);
        char c1 = word.charAt(word.length() - 1);
        if ( (c == 'a' || c  == 'e' || c == 'i' || c == 'o' || c == 'u') 
            && (c1 == 'a' || c1  == 'e' || c1 == 'i' || c1 == 'o' || c1 == 'u')){
            return true;
        }
        return false;
    }
}


。复杂度


  • 时间复杂度:O ( n )
  • 空间复杂度:O ( n )


打家劫舍 IV【LC6346】


沿街有一排连续的房屋。每间房屋内都藏有一定的现金。现在有一位小偷计划从这些房屋中窃取现金。


由于相邻的房屋装有相互连通的防盗系统,所以小偷 不会窃取相邻的房屋


小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额


给你一个整数数组 nums 表示每间房屋存放的现金金额。形式上,从左起第 i 间房屋中放有 nums[i] 美元。


另给你一个整数数组 k ,表示窃贼将会窃取的 最少 房屋数。小偷总能窃取至少 k 间房屋。


返回小偷的 最小 窃取能力。


回溯【超时】


  • 思路:枚举每一种可能的窃取方案,返回最小窃取能力


  • 实现


class Solution {
    int ans = Integer.MAX_VALUE;
    public int minCapability(int[] nums, int k) {
        backtracking(nums, k, 0, 0, 0);
        return ans;
    }
    public void backtracking(int[] nums, int k, int count, int max, int start){
        if (count == k) ans = Math.min(ans, max);
        if (start >= nums.length || start + (k - count - 1) * 2 >= nums.length) return;
        backtracking(nums, k, count, max, start + 1);// 不取
        backtracking(nums, k, count + 1, Math.max(max,nums[start]), start + 2);// 取
    }
}


。复杂度


  • 时间复杂度:O ( n k )
  • 空间复杂度:O ( 1 )


二分答案


卡在了check没想明白


  • 思路:


。由于当窃取能力越大,可以偷取的房屋数越打,因此可以通过二分查找搜索最小窃取能力。


。那么,可以将题意转化为,当偷取房屋数量一定时,寻找最小的窃取能力y ,二分查找的下限为min(nums[i]),上限为max(nums[i])


  • 实现


。当二分查找窃取能力y 是否合法时,需要判断可以偷取的房屋数量是否大于等于k,如果可以偷取的房屋数量大于等于k,那么我们可以减小窃取能力;如果不能,那么增大窃取能力


  • 我们从左到右枚举每座房子,同时记录上一次抢夺的房子的下标 j。如果当前房子可以抢夺(价值小于等于 y,且不和 j 相邻)就进行抢夺。因为越早抢夺,留给右边的抢夺机会就越多。如果抢夺的房屋数量,大于等于k,那么返回true,减小窃取能力,以减小房屋数量;反之返回false,增大窃取能力,以增大房屋数量


class Solution {
    public int minCapability(int[] nums, int k) {
        int n = nums.length;
        int res = Integer.MAX_VALUE;
        int l = Integer.MIN_VALUE, r = 0;
        for (int num : nums){
            r = Math.max(r, num);
            l = Math.min(l, num);            
        }
        while (l <= r){
            int mid = (l + r) / 2;
            if (check(nums, mid, k)){
                r = mid - 1;
                res = Math.min(res, mid);
            }else{
                l = mid + 1;
            }
        }
        return res;
    }
    public boolean check(int[] nums, int target, int k){
        int n = nums.length;
        int j = -2;
        int count = 0;
        for (int i = 0; i < n; i++){
            if (j + 2 <= i && nums[i] <= target){
                count++;
                j = i;
                if (count >= k) return true;
            }     
        }
        return false;
    }
}


。复杂度


  • 时间复杂度:O(nlogmax),max为nums中的最大值
  • 空间复杂度:O ( 1 )


重排水果【LC6345】


你有两个果篮,每个果篮中有 n 个水果。给你两个下标从 0 开始的整数数组 basket1 和 basket2 ,用以表示两个果篮中每个水果的成本。


你希望两个果篮相等。为此,可以根据需要多次执行下述操作:


  • 选中两个下标 i 和 j ,并交换 basket1 中的第 i 个水果和 basket2 中的第 j 个水果。
  • 交换的成本是 min(basket1i,basket2j) 。


根据果篮中水果的成本进行排序,如果排序后结果完全相同,则认为两个果篮相等。


返回使两个果篮相等的最小交换成本,如果无法使两个果篮相等,则返回 -1 。


卡在了越界


  • 思路:【贪心】


。首先使用哈希表记录basket1 和 basket2 中不相同的数及其个数


  • 个数为正数表示该水果在basket1中
  • 个数为负数表示该水果在basket2中


。然后遍历哈希表,如果个数为奇数,那么无法使两个果篮相等,直接返回-1;如果个数为偶数,那么放入相应的堆中,贪心交换果篮,一个堆从小到大排序,另一个堆从大到小排序


  • 局部最优:每次交换的交换成本最小,此时的交换方式有两种


  • 直接将果篮的待交换的最小成本val和另一个果篮的待交换的最大成本进行交换
  • 使用一个中间值交换两次,即果篮中的最小成本m,此时的交换成本为2∗m


因此每次交换的最小成本为min(val,2∗m)


  • 全局最优:当两个果篮相等时,交换成本最小


  • 实现


class Solution {
    public long minCost(int[] basket1, int[] basket2) {
        Map<Integer,Integer> map = new HashMap<>();
        int n = basket1.length;
        // 记录每个成本对应的次数
        for (int i = 0; i < n; i++){
            map.put(basket1[i], map.getOrDefault(basket1[i], 0) + 1);
            map.put(basket2[i], map.getOrDefault(basket2[i], 0) - 1);
        }       
        PriorityQueue<int[]> pq1 = new PriorityQueue<>((o1,o2) -> o1[0] - o2[0]);// 果篮1待交换的成本
        PriorityQueue<int[]> pq2 = new PriorityQueue<>((o1,o2) -> o2[0] - o1[0]);// 果篮2待交换的成本
        int min = Integer.MAX_VALUE;// 两个果篮中的最小成本
        for (var node : map.entrySet()){
            int key = node.getKey(), val = node.getValue();
            min = Math.min(min, key);
            if (val % 2 != 0) return -1;
            if (val > 0){// 果篮1
                pq1.add(new int[]{key, val});
            }else if (val < 0){// 果篮2
                pq2.add(new int[]{key, -val});
            }
        }
        long res = 0L;
        while (!pq1.isEmpty()){// 两个堆一定同时为空
            int[] poll1 = pq1.poll();
            int[] poll2 = pq2.poll();
            // 测试用例35错误 结果为负数,越界
            // int count = Math.min(poll1[1] / 2, poll2[1] / 2);
            long count = Math.min(poll1[1] / 2, poll2[1] / 2);
            res += Math.min(Math.min(poll1[0], poll2[0]) * count, min * count * 2);
            poll1[1] -= count * 2;
            poll2[1] -= count * 2;
            if (poll1[1] != 0){                
                pq1.add(poll1);
            } 
            if (poll2[1] != 0){                
                pq2.add(poll2);
            }
        }
        return res;
    }
}


。复杂度


  • 时间复杂度:O(n+logn),遍历数组和交换水果的时间复杂度为O(n),向优先队列存放元素的时间复杂度为O(logn)


  • 空间复杂度:O ( n )


  • 优化:假设果篮1中待交换的水果有count个,由于在交换过程中val为两个果篮待交换成本中最小的成本,因此可以将两个果篮中待交换的水果拼接在一起,遍历前count个最小的数即可


class Solution {
    public long minCost(int[] basket1, int[] basket2) {
        var cnt = new HashMap<Integer, Integer>();
        for (int i = 0; i < basket1.length; ++i) {
            cnt.merge(basket1[i], 1, Integer::sum);
            cnt.merge(basket2[i], -1, Integer::sum);
        }
        int mn = Integer.MAX_VALUE;
        var a = new ArrayList<Integer>();
        for (var e : cnt.entrySet()) {
            int x = e.getKey(), c = e.getValue();
            if (c % 2 != 0) return -1;
            mn = Math.min(mn, x);
            for (c = Math.abs(c) / 2; c > 0; --c)
                a.add(x);
        }
        long ans = 0;
        a.sort((x, y) -> x - y); // 也可以用快速选择
        for (int i = 0; i < a.size() / 2; ++i)
            ans += Math.min(a.get(i), mn * 2);
        return ans;
    }
}
作者:灵茶山艾府
链接:https://leetcode.cn/problems/rearranging-fruits/solutions/2093878/tan-xin-gou-zao-by-endlesscheng-c2ui/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


。复杂度


  • 时间复杂度:O ( n l o g n )
  • 空间复杂度:O ( n )
目录
相关文章
|
7月前
【周赛总结】周赛354
【周赛总结】周赛354
49 0
|
7月前
13-周赛347总结
13-周赛347总结
55 0
|
7月前
14-周赛348总结
14-周赛348总结
53 0
|
7月前
|
人工智能 BI
12-周赛338总结
12-周赛338总结
52 0
|
7月前
9-周赛335总结
9-周赛335总结
52 0
|
7月前
|
存储
10-周赛336总结
10-周赛336总结
61 0
|
7月前
|
算法 Windows
【周赛总结】周赛360
【周赛总结】周赛360
48 0
|
7月前
【周赛总结】18-周赛353
【周赛总结】18-周赛353
61 0
|
7月前
【周赛总结】周赛356
【周赛总结】周赛356
57 0
|
7月前
|
算法
8-周赛334总结
8-周赛334总结
50 0