LeetCode 力扣周赛 267

1,428 阅读2分钟

这次的题,怎么说呢,就写起来很难受。尤其第三题,题意很模糊,只能靠猜。

当然,大佬们还是很快,我还是太菜了。

周赛传送门

5926. 买票需要的时间

方法一

思路:模拟

时间复杂度O(tickets)O(\sum tickets)

直观的思路是:使用一个队列模拟买票的整个过程,直到第 kk 个人买票完成。

class Solution {
public:
    int timeRequiredToBuy(vector<int>& tickets, int k) {
        // 记录答案
        int anw = 0;

        // 用队列模拟买票过程
        queue<int> q;
        // 初始化队列
        for (auto t : tickets) {
            q.push(t);
        }

        while (q.size() > 0) {
            // 排在队首的人买一张票
            q.front()--;
            // 花费一秒时间
            anw ++;
            if (q.front() != 0) {
                // 如果队首的人未买完,则排到队尾
                q.push(q.front());
                q.pop();
            } else {
                // 如果队首的人买完了,则离队。
                q.pop();
                // 如果是第 k 个人,则退出循环。
                if (k == 0) {
                    break;
                }
            }
            // 更新第 k 个人在队列中的位置。
            if (k == 0) {
                k = q.size()-1;
            } else {
                k--;
            }
        }
        return anw;
    }
};

方法二

思路:找规律

时间复杂度O(n)O(n)

考虑第 kk 人买完票时:

  • 对于 i[0,k)i\in[0,k),必然都买了 min(ticketsi,ticketsk)\min(tickets_i,tickets_k) 张票。
  • 对于 i(k,n)i\in(k,n),必然都买了 min(ticketsi,ticketsk1)\min(tickets_i,tickets_k-1) 张票。

因此,两部分的累加和即为答案。仅需遍历一次 ticketstickets 即可求得。

class Solution {
public:
    int timeRequiredToBuy(vector<int>& tickets, int k) {
        int sum = 0;
        for (int i = 0; i <= k; i++) {
            sum += min(tickets[i], tickets[k]);
        }
        for (int i = k+1; i < tickets.size(); i++) {
            sum += min(tickets[i], tickets[k]-1);
        }
        return sum;
    }
};

5927. 反转偶数长度组的节点

思路:偷懒了,借助数组实现了

时间复杂度O(n)O(n)

竞赛嘛,怎么快怎么搞了。借助数组实现翻转更易实现,只是需要额外的 O(n)O(n) 的存储空间。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseEvenLengthGroups(ListNode* head) {
        // 将节点都放入数组中
        vector<ListNode*> vec;
        for (auto h = head; h != nullptr; h = h->next) {
            vec.push_back(h);
        }

        for (int i = 1, l = 0, r = 1; l < vec.size(); i++) {
            if ((r-l)%2 == 0) {
                // 数组的翻转就很简单啦,
                reverse(vec.begin()+l, vec.begin()+r);
            }
            l = r;
            r = min(int(vec.size()), l+i+1);
        }
        // 按序修改 next 指针即可。
        for (int i = 0; i+1 < vec.size(); i++) {
            //cout << i << ", " << vec[i]->val << endl;
            vec[i]->next = vec[i+1];
        }
        vec.back()->next = nullptr;
        return head;
    }
};

5928. 解码斜向换位密码

思路:模拟

时间复杂度O(n)O(n)

模拟解码的过程即可,详见注释。

class Solution {
public:
    string decodeCiphertext(string encodedText, int rows) {
        // 首先计算出辅助矩阵的行和列
        int r = rows;
        int c = encodedText.size()/r;
        // 定义anw用于存储答案
        string anw;
        // 按照题意,只有斜向位置处的字符有意义。
        // 又根据题意得知,斜向位置(i,j) 在 encodedText 中的位置为 (c*j + j+i)。
        // 因此按序将这些斜向位置处的字符 encodedText[c*j+j+i] 追加至 anw 即可。
        for (int i = 0; i < c; i++) {
            for (int j = 0; j < r && j+i < c; j++) {
                anw += encodedText[c*j + j+i];
            }
        }
        // 最后有个特殊处理,需将尾部的空格全部删除。
        // 不过我在题目上并未读出此要求,比赛时纯属蒙对了。
        while(anw.back() == ' ') {
            anw.pop_back();
        }
        return anw;
    }
};

5929. 处理含限制条件的好友请求

思路:并查集

时间复杂度O(n+reqres)O(n+req*res)reqreq 为请求数量,resres 为限制数量

并查集模板题啦。每次合并之前,先检查是否会违反限制即可。详见注释。

class Solution {
public:
    // 存储并查集的数组
    int fa[1000];
    // 并查集的 find 函数(带有路径压缩)
    int find (int r) {
        int x = r;
        while (x != fa[x]) {
            x = fa[x];
        }
        while (r != fa[r]) {
            int t = fa[r];
            fa[r] = x;
            r = t;
        }
        return x;
    }

    vector<bool> friendRequests(int n, vector<vector<int>>& restrictions, vector<vector<int>>& requests) {
        // 初始化并查集
        for (int i = 0; i < n; i++) {
            fa[i] = i;
        }
        // anw 用于存储答案
        vector<bool> anw;

        // 按序处理所有请求
        for (const auto req : requests) {
            int u = req[0];
            int v = req[1];
            // 分别查找 u 和 v 所在的集合
            int fu = find(u), fv = find(v);

            if (fu == fv) {
                // 根据并查集的定义,如果 fu == fv,则 u 和 v 已处于相同集合,无需处理了。
                anw.push_back(true);
            } else {
                // 检查所有的 res,判断合并 fu 和 fv 后,是否会违反限制
                bool flag = true;
                for (const auto &res : restrictions) {
                    int ru = res[0], rv = res[1];
                    int fru = find(ru), frv = find(rv);
                    // ru 和 rv 恰好分属 fu 和 fv 两个集合,则显然不能合并啦🙅‍♀️
                    if ((fru == fu && frv == fv) || (fru == fv && frv == fu)) {
                        flag = false;
                        break;
                    }
                }
                anw.push_back(flag);
                if (flag) {
                    // 能合则合
                    fa[fu] = fv;
                }
            }
        }
        return anw;
    }
};