2022年6月周赛习题笔记

目录

  • 6月4日:ACwing第54场周赛
    • AcWing 4428. 字符串
    • AcWing 4429. 无线网络
    • AcWing 4430. 括号序列
  • 6月5日:LeetCode 第 296 场周赛
    • 6090. 极大极小游戏
    • 6091. 划分数组使最大差为 K
    • 6092. 替换数组中的元素
    • 6093. 设计一个文本编辑器
  • 6月11日:LeetCode 第 80 场双周赛
    • 2299. 强密码检验器 II
    • 2300. 咒语和药水的成功对数
    • 2301. 替换字符后匹配
    • 2302. 统计得分小于 K 的子数组数目
  • 6月11日:ACwing 第 55 场周赛
    • AcWing 4479. 最长子序列
    • AcWing 4480. 倒垃圾
    • AcWing 4481. 方格探索
  • 6月12日:LeetCode 第 297 场周赛
    • 2303. 计算应缴税款总额
    • 2304. 网格中的最小路径代价
    • 2305. 公平分发饼干
    • 2306. 公司命名

6月4日:ACwing第54场周赛

AcWing 4428. 字符串

题目链接

#include 
#include 
#include 

using namespace std;

int main()
{
    int n;
    string s;
    cin >> n >> s;
    unordered_set<char> S;
    for (auto c : s)
        S.insert(tolower(c));
    if (S.size() == 26) puts("YES");
    else puts("NO");
    return 0;
}

AcWing 4429. 无线网络

题目链接

选择其中一个基站,每次从大到小枚举该基站的半径,每一次抛出去一个点,用于更新另一个基站的半径。当一个基站的半径固定的时候,另外一个基站的半径也一定是固定的。

并且可以知道,最后的结果一定是一个整数,题目中给出"答案四舍五入到个位"是无用的。因为最终圆的边一定在某一个点上,这个点到圆心的半径一定是一个整数。如果半径大于这个点到圆心的距离,那么这个圆是可以缩小的。

排序 O ( l o g n ) O(logn) O(logn),枚举一个圆的半径 O ( n ) O(n) O(n),求另一个圆的半径 O ( 1 ) O(1) O(1),总的时间复杂度 O ( l o g n ) O(logn) O(logn)

#include 
#include 
#include 

#define x first
#define y second

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 2010;

int n;
PII s1, s2;
PII q[N];

LL dist(PII a, PII b) {
    LL dx = a.x - b.x, dy = a.y - b.y;
    return dx * dx + dy * dy;
}

bool cmp(PII a, PII b) {
    return dist(s1, a) < dist(s1, b);
}

int main() {
    cin >> n >> s1.x >> s1.y >> s2.x >> s2.y;
    for (int i = 0; i < n; i++) cin >> q[i].x >> q[i].y;
    sort(q, q + n, cmp);
    LL res = 1e18, r = 0;
    for (int i = n - 1; i >= 0; i--) {
        res = min(res, dist(q[i], s1) + r);
        r = max(r, dist(q[i], s2));
    }
    res = min(res, r);
    printf("%lld\n", res);
    return 0;
}

AcWing 4430. 括号序列

题目链接

题目给出,只有两种变换方法,要么左括号变成右括号,要么右括号变成左括号,变换之后左右括号数量相同。

首先字符串长度必须为偶数,否则返回0;

然后统计字符串中所有左括号和右括号的数量,分别记为 l 、 r l、r lr。如果它们差的绝对值不为2 ,则一定没有合法方案,输出0。即它们应该满足

  • 如果 l > r l > r l>r,那么应该有 l = r + 2 l = r + 2 l=r+2
  • 如果 l < r l < r l<r,那么应该有 r = l + 2 r = l + 2 r=l+2

当左括号的数量多于右括号的时候,令左括号的值为1,右括号的值为-1,为了满足题目条件对于 s 的任意前缀字符串,其中包含的 ( 的数量均不少于 ) 的数量,可以得出任意前缀字符串的前缀和不能<= 0。从左往右扫描,扫描到第一次出现前缀和< 0,即左括号的数量小于右括号数量少的时候,就需要要修改一个右括号为左括号,这时候可以修改的方案数为前面右括号的数量。对于每一种方案,修改完之后还需要观察后面所有的前缀和是否满足,如果满足,即为答案。

当右括号的数量多于左括号的时候,先将其镜像,然后左右括号互换,将此问题转换为为当左括号的数量多于右括号的情况,用上面的方法求解。

#include 
#include 
#include 

using namespace std;

const int N = 1e6 + 10;

int n;
char s[N];

int work() {
    int cnt = 0, r = 0; // 前缀和cnt,右括号数量r
    for (int i = 0; i < n; i++)
        if (s[i] == '(') cnt++;
        else {
            cnt--; r++;
            if (cnt < 0) {
                cnt += 2;
                for (int j = i + 1; j < n; j++)
                    if (s[j] == '(') cnt++;
                    else {
                        cnt--;
                        if (cnt < 0) return 0;
                    }
                return r; // 前面右括号的数量即为方案数
            }
        }
    return 0;
}

int main() {
    scanf("%d%s", &n, s);
    int l = 0, r = 0;
    for (int i = 0; i < n; i++)
        if (s[i] == '(') l++;
        else r++;
    if (r == l + 2) printf("%d\n", work());
    else if (l == r + 2) {
        reverse(s, s + n);
        for (int i = 0; i < n; i++)
            if (s[i] == '(') s[i] = ')';
            else s[i] = '(';
        printf("%d\n", work());
    } else puts("0");
    return 0;
}

6月5日:LeetCode 第 296 场周赛

6090. 极大极小游戏

题目链接

class Solution {
public:
    vector<int> add(vector<int> q) {
        vector<int> res(q.size() / 2);
        for (int i = 0; i < q.size() / 2; i++) {
            if (i % 2)
                res[i] = max(q[i * 2 + 1], q[2 * i]);
            else res[i] = min(q[i * 2 + 1], q[2 * i]);
        }
        return res;
    }

    int minMaxGame(vector<int> &nums) {
        if (nums.size() == 1) return nums[0];
        vector<int> ans = add(nums);
        while (ans.size() != 1)
            ans = add(ans);
        return ans[0];
    }
};

6091. 划分数组使最大差为 K

题目链接

class Solution {
public:
    int partitionArray(vector<int> &nums, int k) {
        sort(nums.begin(), nums.end());
        int n = nums.size(), i, j, ans = 0;
        for (i = 0; i < n; i = j) {
            for (j = i + 1; j < n && nums[j] - nums[i] <= k; j++);
            ans++;
        }
        return ans;
    }
};

6092. 替换数组中的元素

题目链接

class Solution {
public:
    vector<int> arrayChange(vector<int> &nums, vector <vector<int>> &operations) {
        unordered_map<int, int> mp;
        for (int i = 0; i < nums.size(); i++)
            mp[nums[i]] = i;
        for (auto c: operations) {
            int index = mp[c[0]];
            mp.erase(c[0]);
            mp[c[1]] = index;
            nums[index] = c[1];
        }
        return nums;
    }
};

6093. 设计一个文本编辑器

题目链接

class TextEditor {
public:
    stack<char> a, b;

    TextEditor() {
        a = stack<char>();
        b = stack<char>();
    }

    void addText(string text) {
        for (auto c: text)
            a.push(c);
    }

    int deleteText(int k) {
        int target = k;
        while (k > 0 && !a.empty())
            a.pop(), k--;
        return target - k;
    }

    string cursorLeft(int k) {
        while (!a.empty() && k > 0) {
            b.push(a.top());
            a.pop();
            k--;
        }
        return readLeftChar();
    }

    string cursorRight(int k) {
        while (!b.empty() && k > 0) {
            a.push(b.top());
            b.pop();
            k--;
        }
        return readLeftChar();
    }

    string readLeftChar() {
        if (a.empty()) return "";
        string res = "";
        while (res.size() < 10 && !a.empty()) {
            res += a.top();
            a.pop();
        }
        reverse(res.begin(), res.end());
        // 恢复栈
        for (auto c: res)
            a.push(c);

        return res;
    }
};

6月11日:LeetCode 第 80 场双周赛

2299. 强密码检验器 II

题目链接

class Solution {
public:
    bool strongPasswordCheckerII(string a) {
        if (a.size() < 8) return false;
        set<char> st = {'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '+'};
        bool is_l = false, is_u = false, is_d = false, is_c = false, is_s = false;
        for (auto c: a) {
            if (c >= 'a' && c <= 'z') is_l = true;
            if (c >= 'A' && c <= 'Z') is_u = true;
            if (c >= '0' && c <= '9') is_d = true;
            if (st.count(c)) is_c = true;
        }
        for (int i = 1; i < a.size(); i++)
            if (a[i] == a[i - 1])
                is_s = true;
        if (is_l && is_u && is_d && is_c && !is_s) return true;
        return false;
    }
};

2300. 咒语和药水的成功对数

题目链接

⌈ a b ⌉ = ⌊ a + b − 1 b ⌋ ( a > 0 , b > 0 ) \left \lceil \frac{a}{b} \right \rceil = \left \lfloor \frac{a + b - 1}{b} \right \rfloor(a >0, b > 0) ba=ba+b1(a>0,b>0)

class Solution {
public:
    vector<int> successfulPairs(vector<int>& a, vector<int>& b, long long c) {
        int n = a.size();
        sort(b.begin(), b.end());
        vector<int> res;
        for (int i = 0; i < n; i ++ )
        {
            int k = lower_bound(b.begin(), b.end(), (c + a[i] - 1) / a[i]) - b.begin();
            res.push_back(b.size() - k);
        }    
        return res;
    }
};

2301. 替换字符后匹配

题目链接

class Solution {
public:
    bool matchReplacement(string a, string b, vector <vector<char>> &mappings) {
        unordered_set<int> hash;
        for (auto &p: mappings) hash.insert(p[0] * 256 + p[1]);
        int n = a.size(), m = b.size();
        for (int i = 0; i + m - 1 < n; i++) {
            bool flag = true;
            for (int j = 0; j < m; j++) {
                if (a[i + j] != b[j] && !hash.count(b[j] * 256 + a[i + j])) {
                    flag = false;
                    break;
                }
            }
            if (flag) return true;
        }
        return false;
    }
};

2302. 统计得分小于 K 的子数组数目

题目链接

class Solution {
public:
    long long countSubarrays(vector<int>& nums, long long k) {
        int n = nums.size();
        vector<long long> s(n + 1);
        for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + nums[i - 1];
        long long res = 0;
        for (int i = 1, j = 0; i <= n; i ++ ) {
            while ((s[i] - s[j]) * (i - j) >= k) j ++ ;
            res += i - j;
        }
        return res;
    }
};

6月11日:ACwing 第 55 场周赛

AcWing 4479. 最长子序列

题目链接

#include 
#include 

using namespace std;

const int N = 15;

int n, m;
int a[N], b[N];

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) cin >> a[i];
    for (int i = 0; i < m; i++) cin >> b[i];

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++)
            if (a[i] == b[j]) {
                cout << a[i];
                if (i < n - 1) cout << " ";
                break;
            }
    }
    cout << endl;
    return 0;
}

AcWing 4480. 倒垃圾

题目链接

二分:手写二分 O ( n l o g n ) O(nlogn) O(nlogn)

#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 200010, INF = 2e9;

int n, m;
int c[N], a[N], b[N];
int ans[N];

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n + m; i++) scanf("%d", &c[i]);
    for (int i = 0, j = 0, k = 0; i < n + m; i++) {
        int t; scanf("%d", &t);
        if (t) b[++j] = c[i];
        else a[++k] = c[i];
    }
    b[0] = -INF, b[m + 1] = INF;
    for (int i = 1; i <= n; i++) {
        int l = 0, r = m + 1;
        while (l < r) {
            int mid = (l + r) >> 1;
            if (b[mid] >= a[i]) r = mid;
            else l = mid + 1;
        }
        if ((LL) a[i] - b[r - 1] <= (LL) b[r] - a[i]) ans[r - 1]++;
        else ans[r]++;
    }
    for (int i = 1; i <= m; i++) printf("%d ", ans[i]);
    return 0;
}

二分:库函数二分 O ( n l o g n ) O(nlogn) O(nlogn)

#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 2e5 + 10, INF = 2e9;

int n, m;
int c[N], a[N], b[N];
int ans[N];

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n + m; i++) scanf("%d", &c[i]);
    for (int i = 0, j = 0, k = 0; i < n + m; i++) {
        int t; scanf("%d", &t);
        if (t) b[++j] = c[i];
        else a[++k] = c[i];
    }
    b[0] = -INF, b[m + 1] = INF;
    for (int i = 1; i <= n; i++) {
        int r = lower_bound(b, b + m + 2, a[i]) - b;
        if ((LL) a[i] - b[r - 1] <= (LL) b[r] - a[i]) ans[r - 1]++;
        else ans[r]++;
    }
    for (int i = 1; i <= m; i++) printf("%d ", ans[i]);
    return 0;
}

双指针 O ( n ) O(n) O(n)

#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 200010, INF = 2e9;

int n, m;
int c[N], a[N], b[N];
int ans[N];

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n + m; i++) scanf("%d", &c[i]);
    for (int i = 0, j = 0, k = 0; i < n + m; i++) {
        int t; scanf("%d", &t);
        if (t) b[++j] = c[i];
        else a[++k] = c[i];
    }
    b[0] = -INF, b[m + 1] = INF;
    for (int i = 1, j = 0; i <= n; i++) {
        while (b[j] < a[i]) j++; // 找到第一个比a[i]大的坐标
        int r = j;
        if ((LL) a[i] - b[r - 1] <= (LL) b[r] - a[i]) ans[r - 1]++;
        else ans[r]++;
    }
    for (int i = 1; i <= m; i++) printf("%d ", ans[i]);
    return 0;
}

AcWing 4481. 方格探索

题目链接

首先考虑暴力做法。设状态数组 b o o l   f ( i , j , a , b ) bool \space f(i,j,a,b) bool f(i,j,a,b) 表示当前向左走 a a a次,向右走了 b b b次能否到达 ( i , j ) (i,j) (i,j)这个格子,则有状态转移表示

  • 往上走: f ( i − 1 , j , a , b ) f(i - 1, j, a, b) f(i1,j,a,b)
  • 往下走: f ( i + 1 , j , a , b ) f(i + 1, j, a, b) f(i+1,j,a,b)
  • 往左走: f ( i , j − 1 , a + 1 , b ) f(i, j - 1, a + 1, b) f(i,j1,a+1,b)
  • 往右走: f ( i , j + 1 , a , b + 1 ) f(i, j + 1, a, b + 1) f(i,j+1,a,b+1)

对于每一个状态,看是否满足 a ≥ x , b ≥ y a \ge x, b \ge y ax,by即可,时间复杂度为 O ( n 4 ) O(n^4) O(n4)

优化:将状态表示中的某一维放入值里面,即 f ( i , j , a ) f(i,j,a) f(i,j,a)表示当前往左走 a a a次,往右最少走几次能够到达 ( i , j ) (i,j) (i,j),且状态应该满足 f ( i , j , a ) ≤ y f(i,j,a) \le y f(i,j,a)y,这个时候时间复杂度为 O ( n 3 ) O(n^3) O(n3)

进一步优化:不妨设图中存在两个点 ( r , c ) 、 ( i , j ) (r, c)、(i, j) (r,c)(i,j),且 c < j c < j c<j。现在要从 ( r , c ) (r,c) (r,c),往左走 a a a步,往右走 b b b,最终走到 ( i , j ) (i, j) (i,j),则存在如下关系 j − c = b − a ⇒ a = b − ( j − c ) \begin{aligned} &j - c = b - a \\ \Rightarrow &a= b - (j - c)\\ \end{aligned} jc=baa=b(jc)可见 a 、 b a、b ab之间是正相关的,二者知道其中一个最小值就可以求出另外一个最小值,所以状态表示可以再减少一维,即 f ( i , j ) f(i,j) f(i,j)表示走到 ( i , j ) (i,j) (i,j)这个格子,往右最少走几步。

因为状态之间存在依赖关系,且不是一个拓扑图,也就是各个状态之间存在环,所以应该考虑最短路求解而不是DP。

将图中每个格子看成一个点,若图中两个格子之间可以相互到达,即都是空格,则在这两个点之间建立一条有向边,若是往上、下、左走,边权为 0 0 0,只有往右走,边权为 1 1 1,所以问题就转换为了从 ( r , c ) (r,c) (r,c)走到 ( i , j ) (i,j) (i,j)的最短距离。因为图中边权仅含 0 、 1 0、1 01,这样的最短路径可以使用双端队列BFS求解。

#include 
#include 
#include 
#include 

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 2010;

int n, m, sx, sy, X, Y;
int dist[N][N];
char g[N][N];
bool st[N][N];

void bfs() {
    memset(dist, 0x3f, sizeof dist);
    dist[sx][sy] = 0;
    deque<PII> q;
    q.push_back({sx, sy});

    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    while (q.size()) {
        auto t = q.front(); q.pop_front();

        if (st[t.x][t.y]) continue;
        st[t.x][t.y] = true;

        for (int i = 0; i < 4; i++) {
            int x = t.x + dx[i], y = t.y + dy[i];
            if (x < 0 || x >= n || y < 0 || y >= m || g[x][y] == '*') continue;
            int w = 0;
            if (i == 1) w = 1; // 向右走为1
            if (dist[x][y] > dist[t.x][t.y] + w) {
                dist[x][y] = dist[t.x][t.y] + w;
                if (w) q.push_back({x, y});
                else q.push_front({x, y});
            }
        }
    }
}

int main() {
    scanf("%d%d%d%d%d%d", &n, &m, &sx, &sy, &X, &Y);
    sx--, sy--;

    for (int i = 0; i < n; i++) scanf("%s", g[i]);

    bfs();

    int res = 0;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++) {
            int b = dist[i][j];
            if (b <= Y && b - (j - sy) <= X)
                res++;
        }

    printf("%d\n", res);
    return 0;
}

6月12日:LeetCode 第 297 场周赛

2303. 计算应缴税款总额

题目链接

2304. 网格中的最小路径代价

题目链接

2305. 公平分发饼干

题目链接

2306. 公司命名

题目链接

你可能感兴趣的:(c++,算法)