ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

双周赛 51,单周赛 239 题解

2021-05-02 22:02:52  阅读:117  来源: 互联网

标签:arr 题意 int 题解 ans 51 双周 queries


目录

本场比赛涵盖很多知识点,具体为 dfs,构造,双指针,set,单调队列,排列距离,逆序数

双周赛 \(51\)

将所有数字用字符替换

题意

给定一个下标从 \(0\) 开始的字符串 s,其中偶数下标处为小写字母 alpha,奇数下标处为数字 num,现在将所有奇数下标的数字替换成小写字母 alpha + num,保证 alpha + num <= 'z'

例如 a1b2,将被替换成 abbda1c1e1 将被替换成 abcdef

题解

签到,按照题意模拟

class Solution {
public:
    string replaceDigits(string s) {
        for (int i = 0; i < s.length(); ++i)
            if (i % 2 == 1) s[i] = s[i - 1] + s[i] - '0';
        return s;
    }
};

座位预约管理系统

题意

设计一个管理 \(n\) 个座位的系统,座位从 \(1\) 编号到 \(n\)

请你实现 SeatManager 类

  • SeatManager(int n) 初始化一个 SeatManager 对象,它管理从 1 到 n 编号的 n 个座位。所有座位初始都是可预约的。
  • int reserve() 返回可以预约座位的最小编号,并且此座位变为不可预约。
  • void unreserve(int seatNumber) 将给定编号  seatNumber 对应的座位变成可以预约。

\(1\leq n\leq 100,000\)

题解

维护一个 set,初始化时将 \(1\) 到 \(n\) 全部扔到 set 中,预约时返回 set.begin(),同时删除 set.begin(),取消预约时 set.insert(seatnumver)

class SeatManager {
public:
    set<int> st;
    SeatManager(int n) {
        for (int i = 1; i <= n; ++i)
            st.insert(i);
    }
    
    int reserve() {
        int temp = *st.begin();
        st.erase(st.begin());
        return temp;
    }
    
    void unreserve(int seatNumber) {
        st.insert(seatNumber);
    }
};

减小和重新排列数组后的最大元素

题意

给一个正整数数组 arr,可以执行一些操作(也可以不进行任何操作),使得数组满足以下条件

  • arr 中第一个元素必须为 1
  • 任意相邻两个元素的差的绝对值小于等于 1 ,也就是说,对于任意的 1 <= i < arr.length(数组下标从 0 开始),都满足 abs(arr[i] - arr[i - 1]) <= 1abs(x) 为 x 的绝对值。

你可以执行以下 2 种操作任意次数

  • 减小 arr 中任意元素的值,使其变为一个更小的正整数
  • 重新排列 arr 中的元素,你可以以任意顺序重新排列。

请你返回执行以上操作后,在满足前文所述的条件下,arr 中可能的最大值。

\(1\leq arr.length\leq 100,000\)

题解

既然可以重排列,那么我们可以对数组排序

我们可以把任意元素变小,那么只需要把数组头设为 1,剩下的元素取 min(arr[i - 1] + 1, arr[i]) 即可

class Solution {
public:
    int maximumElementAfterDecrementingAndRearranging(vector<int>& arr) {
        sort(arr.begin(), arr.end());
        if (arr[0] != 1) arr[0] = 1;
        for (int i = 1; i < arr.size(); ++i)
            arr[i] = min(arr[i], arr[i - 1] + 1);
        int ans = *max_element(arr.begin(), arr.end());
        return ans;
    }
};

最近的房间

题意

给定 \(n\) 个房间,每个房间的结构为二元组 (roomID, size),分别代表房间 ID 和房间面积,保证房间 ID 两两不同

给 \(k\) 个查询,每个查询为二元组 (preferred, minSize)

现在要求满足 size >= minSIze 同时 abs(roomID - preferred) 最小的房间 ID

如果 abs(roomID - preferred) 相等,那么取小的 roomID,例如 preferred = 4, roomID = 3, 5 的两个房间同时满足题意,选择 roomID = 3,如果没有这样的房间,答案为 -1

\(1\leq n\leq 100,000,\ 1\leq k\leq 10,000\)

题解

如果不考虑面积,只考虑计算最小的 roomID,使得 abs(roomID - preferred) 最小,我们可以维护有序数组 roomID,并二分查找 preferred 前后的两个位置,比较得到最小值,这个可以用 set 维护,lower_bound 计算

考虑面积,若将房间和询问按照面积排序,我们发现倒序枚举询问有奇效。倒序枚举询问时,需求的 minSize 在递减,那么满足 size >= minSize 要求的房间就更多

可以动态维护一个 set,倒序枚举询问,利用双指针不断扩充 set,同时在 set 中二分查找,计算答案

每个房间二元组都只扫描一次,时间复杂度 \(O(nlogn + \sum\limits_{i = 1}^{n}{log_{2}i}) = O(nlogn)\)

#define pb push_back
class Solution {
public:
    static bool cmp(vector<int> &x, vector<int> &y)
    {
        if (x[1] == y[1]) return x[0] < y[0];
        return x[1] < y[1];
    }
    vector<int> closestRoom(vector<vector<int>>& rooms, vector<vector<int>>& queries) {
        int n = rooms.size();
        int k = queries.size();
        for (int i = 0; i < n; ++i) rooms[i].pb(i);
        for (int i = 0; i < k; ++i) queries[i].pb(i);
        vector<int> ans(k);
        sort(rooms.begin(), rooms.end(), cmp);
        sort(queries.begin(), queries.end(), cmp);
        set<int> st;
        for (int i = k - 1, j = n - 1; i >= 0; --i) {
            while (j >= 0 && rooms[j][1] >= queries[i][1]) st.insert(rooms[j--][0]);
            int res = queries[i][2];
            if (st.empty()) ans[res] = -1;
            else {
                auto it = st.lower_bound(queries[i][0]);
                if (it == st.end()) ans[res] = *(--it);
                else if (it == st.begin()) ans[res] = *it;
                else {
                    int x = *it, y = *(--it);
                    int dis1 = abs(x - queries[i][0]), dis2 = abs(y - queries[i][0]);
                    if (dis1 < dis2) ans[res] = x;
                    else ans[res] = y;
                }
            }
        }
        return ans;
    }
};

单周赛 \(239\)

到目标元素的最小距离

题意

给定一个整数数组 nums,以及两个整数 target, start,找出一个下标 i,满足 nums[i] == target 同时 abs(i - start) 最小化

题解

签到,按题意模拟

class Solution {
public:
    int getMinDistance(vector<int>& nums, int target, int start) {
        int n = nums.size();
        int ans = INT_MAX;
        for (int i = 0; i < n; ++i)
            if (target == nums[i]) ans = min(ans, abs(start - i));
        return ans;
    }
};

将字符串拆分为递减的连续值

题意

给定一个仅由数字组成的字符串 s

请判断能否将 s 分割成两个或多个非空子串(连续子序列,使得子串的 数字 按照 降序 排列,且相邻数字的差为 1

  • 例如 009008 可以划分成 9, 8
  • 例如 200100 可以划分成 2, 1
  • 例如 1234 无法被划分

\(1\leq n\leq 20\)

题解

数据很小,考虑搜索

pre 记录之前的值,cur 记录当前的值,cnt 记录划分的子串个数

每一层 dfs 枚举隔板的位置,具体来说,如果 cnt == 0 或者 pre == cur + 1,就可以防止隔板,进行新一轮的划分

数据很大,得用高科技

细节较多,好想不好写

#define ull unsigned long long
class Solution {
public:
    bool dfs(ull pre, int p, string s, int cnt)
    {
        if (p == s.length()) return cnt > 1;
        ull cur = 0;
        for (int i = p; i < s.length(); ++i) {
            cur = cur * 10 + s[i] - '0';
            if (!cnt || cur == pre - 1) { // 符合条件,就试一下能不能继续划分
                if (dfs(cur, i + 1, s, cnt + 1))
                    return 1;
            }
        }
        return 0;
    }
    bool splitString(string s) {
        return dfs(0, 0, s, 0);
    }
};

邻位交换的最小次数

题意

给一个表示大整数的字符串 num,和一个整数 k

如果某个整数是 num 中各位数字的一个排列,且它的值大于 num,则称这个整数为 妙数,我们关注值 最小 的妙数

返回要得到第 k最小妙数,需要对 num 执行的相邻位数字交换的最小次数。

题解

最小妙数可以用 next_permutation 解决

下面考虑最小交换次数,即为 排列的距离

考虑 各位不同 的整数 p,以及其排列 q,计算从 pq 相邻数字的最小交换次数

有一个结论,若要使得任意排列有序,那么相邻数字交换的最小次数为逆序数

作下标映射 p[i] -> i,那么 p 映射成 1, 2, 3..., n,再计算出 q 中对应的下标,求出 q 的逆序数即可

例如,p = 4312, q = 1243 ,作下标映射,那么 p = 1234, q = 3412,得到逆序数为 4

考虑 存在相同位,令相同位的 相对顺序不改变,例如 12322 -> 32122123222 的下标分别为 2, 4, 5,那么 32122 中对应的下标为 32145,逆序数为 3

本题带点思维含量,知道逆序数的结论就好做了

#define pb push_back
class Solution {
public:
    int getInvNum(vector<int> s)
    {
        int n = s.size();
        int num = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                if (s[i] > s[j]) num++;
            }
        }
        return num;
    }
    int getMinSwaps(string num, int k) {
        string s = num;
        int n = num.length();
        while (k--) next_permutation(s.begin(), s.end());
        vector<int> mp[10];
        for (int i = 0; i < n; ++i)
            mp[s[i] - '0'].pb(i);
        vector<int> a(n);
        vector<int> idx(10);
        for (int i = 0; i < n; ++i) {
            a[i] = mp[num[i] - '0'][idx[num[i] - '0']];
            idx[num[i] - '0']++;
        }
        return getInvNum(a);
    }
};

包含每个查询的最小区间

题意

给定一个二维数组 intervals,每个元素是个二元组 (left, right),表示一个 闭区间

给定一个询问数组 queries,第 j 个查询的答案是满足 lefti <= queries[j] <= righti 的长度最小的区间 i 的长度。如果不存在这样的区间,那么答案是 -1

\(1\leq intervals.size\leq 100,000\)

\(1\leq queries.size\leq 100,000\)

题解

本题和 最远的房间 有异曲同工之妙,具体维护一个存放区间长度和区间右端点的 单调队列

intervalsqueries 排序,考虑 顺序枚举查询,动态维护单调队列 set<pair<int, int>>,其中 pair.first 记录区间长度,pair.second 记录区间右端点

  • 考虑入队,设区间长度 Li = intervals[i][1] - intervals[i][0] + 1,利用双指针,将所有满足 intervals[i][0] <= queries[j]Li, intervals[i][1] 打包成二元组放入 set。由于是顺序枚举,后面的查询值一定大于队列中所有区间的左端点
  • 考虑出队,需要把队列中所有 set.begin()->second < queries[j] 的二元组剔除,因为他们的右端点小于目标值
  • 考虑查询,返回 set.begin()->first

每个区间最多入队一次,出队一次,单次复杂度为 \(O(logn)\),因此总的时间复杂度为 \(O(nlogn)\)

#define pb push_back
class Solution
{
public:
    vector<int> minInterval(vector<vector<int>> &intervals, vector<int> &queries)
    {
        int n = intervals.size(), k = queries.size();
        vector<int> idx(k), ans(k);
        for (int i = 0; i < k; ++i) idx[i] = i;
        sort(idx.begin(), idx.end(), [&](int x, int y)
        {
            return queries[x] < queries[y];
        });
        sort(intervals.begin(), intervals.end());
        set<pair<int, int>> st;
        int j = 0;
        for (auto &i: idx) {
            int tar = queries[i];
            while (j < n && intervals[j][0] <= tar) {
                st.emplace(intervals[j][1] - intervals[j][0] + 1, intervals[j][1]);
                ++j;
                
            }
            while (!st.empty() && st.begin()->second < tar)
                st.erase(st.begin());
            if (st.empty()) ans[i] = -1;
            else ans[i] = st.begin()->first;
        }
        return ans;
    }
};

标签:arr,题意,int,题解,ans,51,双周,queries
来源: https://www.cnblogs.com/ChenyangXu/p/14726575.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有