Leetcode 第 412 场周赛题解

Leetcode 第 412 场周赛题解

题目1:3264. K 次乘运算后的最终数组 I

思路

操作:遍历数组 nums,找到其中的最小值 x,如果存在多个最小值,选择最前面的一个。将它乘以 multiplier。

共执行 k 次操作。

代码

/*
 * @lc app=leetcode.cn id=3264 lang=cpp
 *
 * [3264] K 次乘运算后的最终数组 I
 */

// @lc code=start
class Solution
{
public:
    vector<int> getFinalState(vector<int> &nums, int k, int multiplier)
    {
        int n = nums.size();
        while (k--)
        {
            int min_idx = 0;
            for (int i = 1; i < n; i++)
                if (nums[i] < nums[min_idx])
                    min_idx = i;
            nums[min_idx] *= multiplier;
        }
        return nums;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(n * k),其中 n 是数组 nums 的长度。

空间复杂度:O(1)。

题目2:3265. 统计近似相等数对 I

思路

暴力枚举数组 nums 中下标 i 和 j 满足 i < j 的 nums[i] 和 nums[j],判断它们是否近似相等。

细节:先对数组 nums 升序排序,在判断它们是否近似相等,转成字符串进行比较,且只交换较大数的数位。

代码

/*
 * @lc app=leetcode.cn id=3265 lang=cpp
 *
 * [3265] 统计近似相等数对 I
 */

// @lc code=start
class Solution
{
public:
    int countPairs(vector<int> &nums)
    {
        int n = nums.size();
        sort(nums.begin(), nums.end());

        int ans = 0;
        for (int i = 0; i < n - 1; i++)
            for (int j = i + 1; j < n; j++)
                if (check(to_string(nums[i]), to_string(nums[j])))
                    ans++;
        return ans;
    }
    // 辅函数
    bool check(string s1, string s2)
    {
        if (stoi(s1) == stoi(s2))
            return true;
        int len2 = s2.length();
        for (int i = 0; i < len2 - 1; i++)
            for (int j = i + 1; j < len2; j++)
            {
                swap(s2[i], s2[j]); // 交换数位
                if (stoi(s1) == stoi(s2))
                    return true;
                else
                {
                    // 记得复原
                    swap(s2[i], s2[j]);
                }
            }
        return false;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(nlogn + n2 * len(max(nums))2),其中 n 是数组 nums 的长度。

空间复杂度:O(1)。

题目3:3266. K 次乘运算后的最终数组 II

思路

用最小堆手动模拟操作,直到原数组的最大值 mx 成为这 n 个数的最小值。

设此时还剩下 k 次操作,那么:

  • 对于前 k mod n 小的数,还可以再操作 k/n+1 次。
  • 其余元素,还可以再操作 k/n 次。

用快速幂计算操作这么多次后的结果。

代码

/*
 * @lc app=leetcode.cn id=3266 lang=cpp
 *
 * [3266] K 次乘运算后的最终数组 II
 */

// @lc code=start
class Solution
{
private:
    const int MOD = 1e9 + 7;
    // 快速幂
    long long pow(long long x, int n)
    {
        long long res = 1;
        for (; n; n /= 2)
        {
            if (n % 2)
                res = res * x % MOD;
            x = x * x % MOD;
        }
        return res;
    }

public:
    vector<int> getFinalState(vector<int> &nums, int k, int multiplier)
    {
        // 特判
        if (multiplier == 1)
            return move(nums);

        int n = nums.size();
        int mx = *max_element(nums.begin(), nums.end());
        vector<pair<long long, int>> h(n);
        for (int i = 0; i < n; i++)
        {
            h[i] = {nums[i], i};
        }
        ranges::make_heap(h, greater<>()); // 最小堆,O(n) 堆化

        // 模拟,直到堆顶是 mx
        for (; k && h[0].first < mx; k--)
        {
            ranges::pop_heap(h, greater<>());
            h.back().first *= multiplier;
            ranges::push_heap(h, greater<>());
        }

        // 剩余的操作可以直接用公式计算
        ranges::sort(h);
        for (int i = 0; i < n; i++)
        {
            auto &[x, j] = h[i];
            nums[j] = x % MOD * pow(multiplier, k / n + (i < k % n)) % MOD;
        }
        return move(nums);
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(n * logn * logmU),其中 n 是数组 nums 的长度,U=max(nums),m=multiplier。

空间复杂度:O(n),其中 n 是数组 nums 的长度。

题目4:3267. 统计近似相等数对 II

思路

按题目二的做法,再加二层循环做第二次交换,再比较:

/*
 * @lc app=leetcode.cn id=3267 lang=cpp
 *
 * [3267] 统计近似相等数对 II
 */

// @lc code=start
class Solution
{
public:
    int countPairs(vector<int> &nums)
    {
        int n = nums.size();
        sort(nums.begin(), nums.end());

        int ans = 0;
        for (int i = 0; i < n - 1; i++)
            for (int j = i + 1; j < n; j++)
                if (check(to_string(nums[i]), to_string(nums[j])))
                    ans++;
        return ans;
    }
    // 辅函数
    bool check(string s1, string s2)
    {
        if (stoi(s1) == stoi(s2))
            return true;
        int len2 = s2.length();
        for (int i = 0; i < len2 - 1; i++)
            for (int j = i + 1; j < len2; j++)
            {
                swap(s2[i], s2[j]); // 交换数位
                if (stoi(s1) == stoi(s2))
                    return true;
                else
                {
                    for (int p = i + 1; p < len2 - 1; p++)
                        for (int q = p + 1; q < len2; q++)
                        {
                            swap(s2[p], s2[q]); // 交换数位
                            if (stoi(s1) == stoi(s2))
                                return true;
                            else
                            {
                                // 记得复原
                                swap(s2[p], s2[q]);
                            }
                        }
                    // 记得复原
                    swap(s2[i], s2[j]);
                }
            }
        return false;
    }
};
// @lc code=end

这样做会超时:

在这里插入图片描述

把 nums 按照数字长度从小到大排序(也就是元素值从小到大排序),枚举 j 并暴力交换 nums[j] 中的数位。设交换后的数字为 x,统计左边有多少个 nums[i] 等于 x。

为避免重复统计,可以先用一个哈希集合记录交换后的数字,再去遍历哈希集合中的元素 x,统计左边有多少个 nums[i] 等于 x。

代码

/*
 * @lc app=leetcode.cn id=3267 lang=cpp
 *
 * [3267] 统计近似相等数对 II
 */

// @lc code=start
class Solution
{
public:
    int countPairs(vector<int> &nums)
    {
        int n = nums.size();
        sort(nums.begin(), nums.end());

        unordered_map<int, int> cnt;
        int ans = 0;
        for (int &x : nums)
        {
            unordered_set<int> st = {x}; // 不交换
            string s = to_string(x);
            int m = s.length();
            for (int i = 0; i < m; i++)
            {
                for (int j = i + 1; j < m; j++)
                {
                    swap(s[i], s[j]);
                    st.insert(stoi(s)); // 交换一次
                    for (int p = i + 1; p < m; p++)
                    {
                        for (int q = p + 1; q < m; q++)
                        {
                            swap(s[p], s[q]);
                            st.insert(stoi(s)); // 交换两次
                            swap(s[p], s[q]);
                        }
                    }
                    swap(s[i], s[j]);
                }
            }
            for (int v : st)
            {
                ans += cnt.contains(v) ? cnt[v] : 0;
            }
            cnt[x]++;
        }
        return ans;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(nlogn+nlog5U),其中 n 是数组 nums 的长度,U=max(nums)。

空间复杂度:O(n+nlog4U),其中 n 是数组 nums 的长度,U=max(nums)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值