力扣第331场周赛题解

rank T1 T2 T3 T4
331 / 4256 0:01:46 0:06:18 (1) 0:11:58 0:57:29 (4)

T1 从数量最多的堆取走礼物

模拟题,每次操作找出最大值 m a x x maxx maxx并将其变成 s q r t ( m a x x ) sqrt(maxx) sqrt(maxx) k k k次操作后求和。

数据范围很小,可以暴力模拟,更好的方法是使用优先队列模拟。

时间复杂度 O ( m a x ( n , k ) l o g ( n ) ) O(max(n,k)log(n)) O(max(n,k)log(n))
空间复杂度 O ( n ) O(n) O(n)

参考代码

class Solution {
    
    
   public:
    long long pickGifts(vector<int>& a, int k) {
    
    
        priority_queue<int> q;
        for (auto it : a) {
    
    
            q.push(it);
        }
        while (k--) {
    
    
            int s = q.top();
            q.pop();
            s = sqrt(s);
            q.push(s);
        }
        long long ans = 0;
        while (q.size()) {
    
    
            ans += q.top();
            q.pop();
        }
        return ans;
    }
};

T2 统计范围内的元音字符串数

给定一个字符串数组,每次询问区间 [ l , r ] [l,r] [l,r]内,首尾字母都是元音字母的字符串数量。

预处理前缀和,差分询问。

时间复杂度 O ( n + q ) O(n+q) O(n+q)
空间复杂度 O ( n ) O(n) O(n)

参考代码

const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
class Solution {
    
    
   public:
    map<int, int> mp;
    bool check(string s) {
    
    
        return mp[s[0]] && mp[s.back()];
    }
    int sum[N];
    vector<int> vowelStrings(vector<string>& a, vector<vector<int>>& q) {
    
    
        mp['a'] = 1;
        mp['e'] = 1;
        mp['i'] = 1;
        mp['o'] = 1;
        mp['u'] = 1;
        int n = a.size();
        for (int i = 0; i < n; i++) {
    
    
            sum[i + 1] = sum[i] + check(a[i]);
        }
        vector<int> ans;
        for (auto it : q) {
    
    
            ans.emplace_back(sum[it[1] + 1] - sum[it[0]]);
        }
        return ans;
    }
};

T3 打家劫舍 IV

要求最大值最小,经典二分答案+ c h e c k check check
c h e c k check check 思路:二分出 m i d mid mid,贪心地线性检查能否取出 k k k 个价值 < = m i d <=mid <=mid 的房屋即可。

时间复杂度 O ( n l o g ( m ) ) O(nlog(m)) O(nlog(m))
空间复杂度 O ( n ) O(n) O(n)

参考代码

const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;

class Solution {
    
    
   public:
    int minCapability(vector<int>& a, int k) {
    
    
        int n = a.size();
        int l = 1, r = 1e9, ans;
        while (l <= r) {
    
    
            int mid = (l + r) / 2;
            int pre = -2;
            int sum = 0;
            for (int i = 0; i < n; i++) {
    
    
                if (i - pre == 1) {
    
    
                    //不能相邻
                    continue;
                }
                if (a[i] <= mid) {
    
    
                    //选择
                    sum++;
                    pre = i;
                }
            }
            if (sum >= k) {
    
    
                r = mid - 1;
                ans = mid;
            } else {
    
    
                l = mid + 1;
            }
        }
        return ans;
    }
};

T4 重排水果

如下考虑:

  1. 判断原数组是否合法:两个数组排序后相同,则直接返回0。

  2. 判断是否存在解:将两个数组的元素混合在一起,如果某个元素为奇数个,那么一定无解。

  3. 剩下来的是必定有解,但需要交换的情况。
    首先去除他们的交集元素,现在数组 a a a 和数组 b b b 分别多了某些元素。根据每个元素的总数量,容易计算得到他们分别多出来 x i x_i xi y i y_i yi,维护 o u t a outa outa o u t b outb outb o u t a [ x ] = y outa[x]=y outa[x]=y表示数组 a a a x x x元素多了 y y y个。
    考虑如何交换最优:
    ① 用数组 a a a中的最小值和数组 b b b中的最大值交换,花费为 m i n ( a m i n n , b m a x x ) min(a_{minn},b_{maxx}) min(aminn,bmaxx)
    ② 用数组 a a a中的最大值和数组 b b b中的最小值交换,花费为 m i n ( b m i n n , a m a x x ) min(b_{minn},a_{maxx}) min(bminn,amaxx)
    ③ 容易遗漏的一种情况,利用所有元素的最小值作为中间者,间接的交换 a b ab ab中的元素:swap(a,c); swap(b,c);,因为 c c c必定为最小值,且被用到了两次,因此花费为 m i n n ∗ 2 minn*2 minn2
    贪心地,如果数组 a a a中的最小值 < < <数组 b b b中的最小值,则选用方案①③的组合,否则选用方案②③的组合。

时间复杂度 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
空间复杂度 O ( n ) O(n) O(n)

参考代码

const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
class Solution {
    
    
   public:
    long long minCost(vector<int>& a, vector<int>& b) {
    
    
        int MINN = INF;
        map<int, int> mp, ma, mb;
        for (auto it : a) {
    
    
            mp[it]++, ma[it]++;
            MINN = min(MINN, it);
        }
        for (auto it : b) {
    
    
            mp[it]++, mb[it]++;
            MINN = min(MINN, it);
        }
        if (ma == mb) {
    
    
            // 已经合法
            return 0;
        }
        for (auto& it : mp) {
    
    
            if (it.second % 2) {
    
    
                // 某个元素总数量奇数个,不能平分
                return -1;
            }
            // 期望每个数组各有it.second个it.first
            it.second /= 2;
        }
        vector<pair<int, int>> outa, outb;
        int all = 0;
        for (auto it : mp) {
    
    
            int delta = ma[it.first] - it.second;
            // a中需要给出delta个it.first
            if (delta > 0) {
    
    
                // 总共需要给出all个
                all += delta;
                outa.emplace_back(make_pair(it.first, delta));
            }
            delta = mb[it.first] - it.second;
            // b中需要给出delta个it.first
            if (delta > 0) {
    
    
                outb.emplace_back(make_pair(it.first, delta));
            }
        }

        long long ans = 0;
        int n = outa.size();
        int ra = outa.size() - 1;
        int rb = outb.size() - 1;
        int la = 0;
        int lb = 0;

        while (all--) {
    
    
            if (!outa[la].second) {
    
    
                ++la;
            }
            if (!outa[ra].second) {
    
    
                --ra;
            }
            if (!outb[lb].second) {
    
    
                ++lb;
            }
            if (!outb[rb].second) {
    
    
                --rb;
            }
            if (outa[la].first < outb[lb].first) {
    
    
                outa[la].second--;
                outb[rb].second--;
                // 两种情况,借助最小值来间接交换 / 直接交换
                ans += min(min(MINN * 2, outa[la].first), outb[rb].first);
            }
            if (outa[la].first > outb[lb].first) {
    
    
                outa[ra].second--;
                outb[lb].second--;
                ans += min(min(MINN * 2, outa[ra].first), outb[lb].first);
            }
        }
        return ans;
    }
};

猜你喜欢

转载自blog.csdn.net/hesorchen/article/details/128889515