LeetCode 第274场周赛

本次周赛是2022年的第一场周赛。题目链接位于这里

No 1. 检查是否所有 A 都在 B 之前

给你一个 仅 由字符 ‘a’ 和 ‘b’ 组成的字符串 s 。如果字符串中 每个 ‘a’ 都出现在 每个 ‘b’ 之前,返回 true ;否则,返回 false 。

示例 1:
输入:s = “aaabbb”
输出:true
解释:
‘a’ 位于下标 0、1 和 2 ;而 ‘b’ 位于下标 3、4 和 5 。
因此,每个 ‘a’ 都出现在每个 ‘b’ 之前,所以返回 true 。

示例 2:
输入:s = “abab”
输出:false
解释:
存在一个 ‘a’ 位于下标 2 ,而一个 ‘b’ 位于下标 1 。
因此,不能满足每个 ‘a’ 都出现在每个 ‘b’ 之前,所以返回 false 。

示例 3:
输入:s = “bbb”
输出:true
解释:
不存在 ‘a’ ,因此可以视作每个 ‘a’ 都出现在每个 ‘b’ 之前,所以返回 true 。

提示:

1 <= s.length <= 100
s[i] 为 ‘a’ 或 ‘b’

解析

本题给我们一个仅有字符a或b组成的字符串,让我们判断是不是所有的a都在b之前,换言之,是不是最后一个a在第一个b之前。因此我们只要分别从前和后遍历字符串,寻找最后一个a的位置和第一个b的位置,判断是否满足条件即可。
这里要注意,全是a或全是b的字符串仍然视作满足条件,因此我们可以将最后一个a的位置初始化成-1,第一个b的位置初始化为n,这样即使字符串只含一种字母,仍能够直接判断。

C++代码如下:

bool checkString(string s) {
            int n = s.size(), fb=n, la=-1;
    for (int i = 0; i < n; ++i) {
      if (s[i] == 'b') {
        fb = i;
        break;
      }
    }
    for (int i = n - 1; i >= 0; --i) {
      if (s[i] == 'a') {
        la = i;
        break;
      }
    }
    return la < fb;
    }

No 2. 银行中的激光束数量

银行内部的防盗安全装置已经激活。给你一个下标从 0 开始的二进制字符串数组 bank ,表示银行的平面图,这是一个大小为 m x n 的二维矩阵。 bank[i] 表示第 i 行的设备分布,由若干 ‘0’ 和若干 ‘1’ 组成。‘0’ 表示单元格是空的,而 ‘1’ 表示单元格有一个安全设备。

对任意两个安全设备而言,如果同时 满足下面两个条件,则二者之间存在 一个 激光束:

两个设备位于两个 不同行 :r1 和 r2 ,其中 r1 < r2 。
满足 r1 < i < r2 的 所有 行 i ,都 没有安全设备 。
激光束是独立的,也就是说,一个激光束既不会干扰另一个激光束,也不会与另一个激光束合并成一束。

返回银行中激光束的总数量。

示例 1:
在这里插入图片描述

输入:bank = [“011001”,“000000”,“010100”,“001000”]
输出:8
解释:在下面每组设备对之间,存在一条激光束。总共是 8 条激光束:

  • bank[0][1] – bank[2][1]
  • bank[0][1] – bank[2][3]
  • bank[0][2] – bank[2][1]
  • bank[0][2] – bank[2][3]
  • bank[0][5] – bank[2][1]
  • bank[0][5] – bank[2][3]
  • bank[2][1] – bank[3][2]
  • bank[2][3] – bank[3][2]
    注意,第 0 行和第 3 行上的设备之间不存在激光束。
    这是因为第 2 行存在安全设备,这不满足第 2 个条件。

解析

本题给定一个二维数组,表示安全设备。两行安全设备可以组成激光束。由于题目的要求,我们可以得到,2个设备要形成激光束,需要他们在不同的行,且这两行之间要么是相邻的,要么中间都是空行。

假设两行分别含有a和b个设备,那么由于一行的每一个设备都可以和另一行每个设备组成激光束,那么一共会形成 a ∗ b a*b ab个。

显然,激光束只跟行编号有关,跟所处的列无关。那么我们可以先遍历数组,求出每一行的设备数量;又因为空行是被跳过的,所以排除空行。然后相邻两行之间形成激光束,数量是二者设备的乘积。

C++代码如下:

    int numberOfBeams(vector<string>& bank) {
        int n = bank.size(), m = bank[0].size();
    vector<int>rows(n, 0);
    for (int i = 0; i < n; ++i) {
      int tmp = 0;
      for (int j = 0; j < m; ++j) {
        if (bank[i][j] == '1')++tmp;
      }
      rows[i] = tmp;
    }
    int count = 0;
    int i = 0, j = 1;
    while (i < n && j < n) {
      while (j < n && rows[j] == 0)++j;
      if (j < n) {
        count += rows[i] * rows[j];
        i = j;
        j++;
      }
    }
    return count;
    }

上述的代码在统计完每行设备后,采用了双指针方式指示产生激光的两行,并实现空行的跳过。实际也可以遍历一次,删除空行,然后一个指针遍历即可。

No 3. 摧毁小行星

给你一个整数 mass ,它表示一颗行星的初始质量。再给你一个整数数组 asteroids ,其中 asteroids[i] 是第 i 颗小行星的质量。

你可以按 任意顺序 重新安排小行星的顺序,然后让行星跟它们发生碰撞。如果行星碰撞时的质量 大于等于 小行星的质量,那么小行星被 摧毁 ,并且行星会 获得 这颗小行星的质量。否则,行星将被摧毁。

如果所有小行星 都 能被摧毁,请返回 true ,否则返回 false 。

示例 1:

输入:mass = 10, asteroids = [3,9,19,5,21]
输出:true
解释:一种安排小行星的方式为 [9,19,5,3,21] :

  • 行星与质量为 9 的小行星碰撞。新的行星质量为:10 + 9 = 19
  • 行星与质量为 19 的小行星碰撞。新的行星质量为:19 + 19 = 38
  • 行星与质量为 5 的小行星碰撞。新的行星质量为:38 + 5 = 43
  • 行星与质量为 3 的小行星碰撞。新的行星质量为:43 + 3 = 46
  • 行星与质量为 21 的小行星碰撞。新的行星质量为:46 + 21 = 67
    所有小行星都被摧毁。
    示例 2:

输入:mass = 5, asteroids = [4,9,23,4]
输出:false
解释:
行星无论如何没法获得足够质量去摧毁质量为 23 的小行星。
行星把别的小行星摧毁后,质量为 5 + 4 + 9 + 4 = 22 。
它比 23 小,所以无法摧毁最后一颗小行星。

解析

本题给定一个整数表示行星质量,一个数组表示若干小行星。我们可以任意安排小行星的顺序,让行星撞击小行星,如果其质量大于小行星,就摧毁小行星并获取它的质量,求我们能否摧毁全部小行星。换言之,如果数组asteroids 的数a满足 a < = m a s s a<=mass a<=mass,就会被摧毁,同时 m a s s = m a s s + a mass=mass+a mass=mass+a

本题实际是一个贪心的策略。行星按照从小到大的顺序撞击,就是最优的策略。吃掉质量小的并增加自身质量再去撞击质量大的。

因此代码也很简单,对小行星质量数组按照从小到大排序,行星依次对比大小,返回能否全部撞击即可。
这里要注意,由于数据范围,质量不断增加,对于int是有可能溢出的,因此要换用long long 64位的数据类型。

C++代码如下:

 bool asteroidsDestroyed(int mass, vector<int>& asteroids) {
    sort(asteroids.begin(), asteroids.end());
    long long m = (long long)mass;
    int n = asteroids.size();
    int i=0;
      while(i<n&&m>=asteroids[i]){
        m+=(long long)asteroids[i];
        ++i;
      }
      return i==n;
    }

No 4. 参加会议的最多员工数

一个公司准备组织一场会议,邀请名单上有 n 位员工。公司准备了一张 圆形 的桌子,可以坐下 任意数目 的员工。

员工编号为 0 到 n - 1 。每位员工都有一位 喜欢 的员工,每位员工 当且仅当 他被安排在喜欢员工的旁边,他才会参加会议。每位员工喜欢的员工 不会 是他自己。

给你一个下标从 0 开始的整数数组 favorite ,其中 favorite[i] 表示第 i 位员工喜欢的员工。请你返回参加会议的 最多员工数目 。

示例 1:
在这里插入图片描述

输入:favorite = [2,2,1,2]
输出:3
解释:
上图展示了公司邀请员工 0,1 和 2 参加会议以及他们在圆桌上的座位。
没办法邀请所有员工参与会议,因为员工 2 没办法同时坐在 0,1 和 3 员工的旁边。
注意,公司也可以邀请员工 1,2 和 3 参加会议。
所以最多参加会议的员工数目为 3 。

示例 2:
输入:favorite = [1,2,0]
输出:3
解释:
每个员工都至少是另一个员工喜欢的员工。所以公司邀请他们所有人参加会议的前提是所有人都参加了会议。
座位安排同图 1 所示:

  • 员工 0 坐在员工 2 和 1 之间。
  • 员工 1 坐在员工 0 和 2 之间。
  • 员工 2 坐在员工 1 和 0 之间。
    参与会议的最多员工数目为 3 。

解析

本题的解析参考了力扣题解
本题给定了编号为0到n-1的n位员工,以及他们喜欢的员工(一定不是自己)。选取员工围成环形,每个员工要与自己喜欢的员工相邻才可以出席,求最多有多少位员工出席。

由于喜欢是一个单向的过程,因此可以将题目给出的喜欢看做一条有向的边,所有员工和他们的喜欢关系构成了一张有向图。这个有向图可以看做由若干带有一个环和环上的边构成的子图所组成的。换言之若干员工的喜欢关系构成一个环,而环上的节点也可以成为其他环外员工的喜欢目标。
就如题解所示的 基环内向树

那么我们选取出席的员工,就存在两种情况:

  1. 某一个子图的环状部分,每个员工都与喜欢员工相邻;
  2. 一个长度为2的环,加上环上两个节点延伸出的内向树。
    如果要将环外的部分包含进来,环的长度不能超过2.
    例如:
    在这里插入图片描述
    如果我们选取上图ABC这个环和AB外延的树状部分,那么,如果不加入节点C,B找不到喜欢的员工;如果加入,则面临A的两侧要做3个人这样无法实现的情况。

因此,能够出席的情况只有上述两种。

对于环的长度大于等于3,此时只能安排一个环的员工,否则不能首尾相接而破坏喜欢的关系链
对于环的长度等于2,此时可以安排多个长度为2的环,因为二者彼此相邻,无所谓另一侧是谁。因此每个长度为2的环连带选取的树状部分(题解称为 双向游走)是可以共存的。

所以最多的员工选取办法有两种:

  1. 图中最大的环
  2. 所有的长度为2的环以及双向游走的树状部分之和

通过拓扑排序的方式,记录以每个节点为终点的最大游走数量,并弹出非环状部分的员工,从剩下员工中找到每一个环。如果环的长度为2,那么以这两个节点为终点的最大游走数量之和即为这个长度为2的环对应的最多出席员工;将所有长度为2的最多出席员工加起来,就是上述第二种情况的最大值。同时保存更新最大的环的长度。最终返回两种情况中最大的。

代码部分,题解的实现更好一些,可以从上方链接进入参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值