Leetcode 第 138 场双周赛题解

Leetcode 第 138 场双周赛题解

题目1:3270. 求出数字答案

思路

按题意模拟。

代码

/*
 * @lc app=leetcode.cn id=3270 lang=cpp
 *
 * [3270] 求出数字答案
 */

// @lc code=start
class Solution
{
public:
    int generateKey(int num1, int num2, int num3)
    {
        string s1 = trans(num1);
        string s2 = trans(num2);
        string s3 = trans(num3);

        string s = "";
        for (int i = 0; i < 4; i++)
            s += min(s1[i], min(s2[i], s3[i]));
        return stoi(s);
    }
    // 辅函数
    string trans(int x)
    {
        string s = to_string(x);
        int n = s.length();
        for (int i = 0; i < 4 - n; i++)
            s.insert(s.begin(), '0');
        return s;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(1)。

空间复杂度:O(1)。

题目2:3271. 哈希分割字符串

思路

按题意模拟。

代码

/*
 * @lc app=leetcode.cn id=3271 lang=cpp
 *
 * [3271] 哈希分割字符串
 */

// @lc code=start
class Solution
{
public:
    string stringHash(string s, int k)
    {
        int n = s.length();
        string result;
        for (int i = 0; i < n / k; i++)
        {
            int sum = 0;
            for (int j = 0; j < k; j++)
                sum += s[i * k + j] - 'a';
            int hashedChar = sum % 26;
            result.push_back(hashedChar + 'a');
        }
        return result;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(n),其中 n 是字符串 s 的长度。

空间复杂度:O(1),返回值不计入。

题目3:3272. 统计好整数的数目

思路

枚举所有长为 n 的回文数的左半边。

如果回文数 x 能被 k 整除,那么接下来需要解决的问题有两个:

  1. 计算 x 有多少个不同的排列。
  2. 不能重复统计。

统计 s 中的每个数字的出现次数 cnt。

先填最高位。由于不能有前导零,最高位可以填的数有 n−cnt0 个。其余 n−1 个数随便排,有 (n−1)! 种方案。

当然,这里面有重复的,例如 x=34543,其中两个 3 和两个 4 的排列就是重复的,由于这两个 3 无法区分,两个 4 无法区分,方案数要除以 2!2!。

代码

/*
 * @lc app=leetcode.cn id=3272 lang=cpp
 *
 * [3272] 统计好整数的数目
 */

// @lc code=start
class Solution
{
private:
    vector<int> factorial;
    void init(int n)
    {
        factorial.assign(n + 1, 0);
        factorial[0] = 1;
        for (int i = 1; i <= n; i++)
        {
            factorial[i] = factorial[i - 1] * i;
        }
    }

public:
    long long countGoodIntegers(int n, int k)
    {
        init(n);

        long long ans = 0;
        unordered_set<string> vis;
        int base = pow(10, (n - 1) / 2);
        for (int i = base; i < base * 10; i++)
        { // 枚举回文数左半边
            string s = to_string(i);
            s += string(s.rbegin() + (n % 2), s.rend());
            if (stoll(s) % k)
            { // 回文数不能被 k 整除
                continue;
            }

            ranges::sort(s);
            if (!vis.insert(s).second)
            { // 不能重复统计
                continue;
            }

            int cnt[10]{};
            for (char c : s)
            {
                cnt[c - '0']++;
            }
            int res = (n - cnt[0]) * factorial[n - 1];
            for (int c : cnt)
            {
                res /= factorial[c];
            }
            ans += res;
        }
        return ans;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(10m * nlogn),其中 m = (n-1)/2。

空间复杂度:O(10m * n),其中 m = (n-1)/2。

题目4:3273. 对 Bob 造成的最少伤害

思路

贪心。

首先,一直攻击同一个敌人,相比来回攻击多个敌人(雨露均沾)更好,因为这样我们被敌人攻击的次数更少。

从特殊到一般,想一想,如果只有两个敌人 A 和 B,我们应该先攻击谁?

消灭 A 需要攻击的次数为 kA = ceil(healthA / power),同理可得消灭 B 需要的攻击次数,记作 kB

如果先消灭 A,再消灭 B,那么受到的伤害总和为 kA * damageA + (kA + kB) * damageB;如果先消灭 B,再消灭 A,那么受到的伤害总和为 kB * damageB + (kA + kB) * damageA

如果先消灭 A,再消灭 B 更好,那么有 kA * damageA + (kA + kB) * damageB < kB * damageB + (kA + kB) * damageA,化简得 kA * damageB < kB * damageA

推广到更多的敌人,可以按照上式对 damage 和 health 排序,理由如下。

先假定按照输入的顺序消灭敌人。如果发现相邻两个敌人不满足上面的不等式,就交换这两个敌人的位置,这可以让受到的总伤害变小。

不断交换敌人,直到所有相邻敌人都满足上面的不等式。

本质上来说,这个不断交换相邻敌人的过程,和冒泡排序是一样的。那么按照不等式对数组排序即可。

排序后,按照顺序消灭敌人。用一个变量 s 维护从一开始到击败当前敌人,所经过的秒数。把 s * damage[i] 加入答案。

代码

/*
 * @lc app=leetcode.cn id=3273 lang=cpp
 *
 * [3273] 对 Bob 造成的最少伤害
 */

// @lc code=start
class Solution
{
public:
    long long minDamage(int power, vector<int> &damage, vector<int> &health)
    {
        int n = damage.size();
        vector<pair<int, int>> comb(n);
        for (int i = 0; i < n; i++)
        {
            int k = ceil(1.0 * health[i] / power);
            comb[i] = make_pair(k, damage[i]);
        }
        sort(comb.begin(), comb.end(),
             [&](const pair<int, int> &p1, const pair<int, int> &p2)
             { return p1.first * p2.second < p2.first * p1.second; });

        long long ans = 0LL;
        long long s = 0LL;
        for (auto &[k, d] : comb)
        {
            s += k;
            ans += s * d;
        }
        return ans;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(nlogn),其中 n 是数组 damage 的长度。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

UestcXiye

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

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

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

打赏作者

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

抵扣说明:

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

余额充值