当前位置: 首页 > article >正文

Leetcode 第 138 场双周赛题解

Leetcode 第 138 场双周赛题解

  • Leetcode 第 138 场双周赛题解
    • 题目1:3270. 求出数字答案
      • 思路
      • 代码
      • 复杂度分析
    • 题目2:3271. 哈希分割字符串
      • 思路
      • 代码
      • 复杂度分析
    • 题目3:3272. 统计好整数的数目
      • 思路
      • 代码
      • 复杂度分析
    • 题目4:3273. 对 Bob 造成的最少伤害
      • 思路
      • 代码
      • 复杂度分析

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 的长度。


http://www.kler.cn/a/301715.html

相关文章:

  • LLaMA-Factory全流程训练模型
  • 15-1.Java 网络编程之 InetAddress(InetAddress 常用静态方法、InetAddress 常用方法)
  • go 集成swagger 在线接口文档
  • Python作业05
  • Word_小问题解决_1
  • 【大数据学习 | HBASE高级】hive操作hbase
  • 基于物联网的智能控制系统设计方案——物联网智能化控制箱
  • dp + 计数,CF 1920 E - Counting Binary Strings
  • ‌汽车一键式启动系统‌包含哪些功能
  • ARM32开发——DMA
  • C++ 二叉树进阶
  • STM32L051K8U6-HAL-串口中断控制灯闪烁速度
  • php反序列化基础
  • ultralytics实现DeepSort之级联匹配
  • 学习Vue3的第五天
  • MAC 地址简化概念(有线 MAC 地址、无线 MAC 地址、MAC 地址的随机化)
  • Android Radio2.0——有效电台扫描(八)
  • 【网络】高级IO——五种IO模式
  • 概念——二连杆与三连杆解算
  • VS2019界面介绍
  • vue3+ant design vue动态实现级联菜单~
  • Gradle和Maven
  • 第四部分:1---文件内核对象,文件描述符,输出重定向
  • Unity基本操作
  • 前端封装组件可视化库
  • 第15-05章:获取运行时类的完整结构