Leetcode第 313 场周赛

在这里插入图片描述

题目链接

竞赛 - 力扣 (LeetCode)

题目解析

A.6192. 公因子的数目

AC代码

class Solution {
public:
    int commonFactors(int a, int b) {
        int t = std::min(a, b);
        int ans = 0;
        for (int i = 1; i <= t; ++i) {
            if (a % i == 0 && b % i == 0) ++ans;
        }
        return ans;
    }
};

思路分析

没什么说的,数据量很小,闭着眼模拟就是了。

B.2428. 沙漏的最大总和

AC代码

class Solution {
public:
    int maxSum(vector<vector<int>>& grid) {
        constexpr int MAXN = 7;
        array<std::pair<int, int>, MAXN> aux = {{
                {0, 0}, {0, 1}, {0, 2},
                {1, 1},
                {2, 0}, {2, 1}, {2, 2},
        }};
        int n = grid.size() - 2;
        int m = grid[0].size() - 2;
        int ans = -1;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                int tmp = 0;
                for (int k = 0; k < MAXN; ++k) {
                    tmp += grid[i + aux[k].first][j + aux[k].second];
                }
                ans = std::max(ans, tmp);
            }
        }
        return ans;
    }
};

思路分析

仍旧是简单模拟,就是写的有些慢。

C.2429. 最小 XOR

AC代码

class Solution {
public:
    int minimizeXor(int num1, int num2) {
        constexpr int MAXN = 32;
        std::bitset<MAXN> n1(num1), n2(num2), n3;
        int t = n2.count();
        for (int i = MAXN - 1; i >= 0; --i) {
            if (t == 0) break;
            if (n1.test(i)) {
                n3.set(i);
                --t;
            }
        }
        for (int i = 0; i < MAXN; ++i) {
            if (t == 0) break;
            if (!n1.test(i)) {
                n3.set(i);
                --t;
            }
        }
        return static_cast<int>(n3.to_ullong());
    }
};

思路分析

也是一个简单的模拟题:为了让最后的数字^num1最小,就要尽可能消掉高位的1,如果所有的1都消掉了,那么就尽可能填充低位的0。如果用位操作可能还是有些繁琐,容易出错,但是使用bitset直接掉方法,简单又方便。

D.2430. 对字母串可执行的最大删除数

AC代码

class Solution {
    vector<int> memo;
    int aux(const string &s, int begin, int end, int cnt) {
        if (end - begin < 2) return cnt + 1;
        if (~memo[begin]) return memo[begin] + cnt;
        int mid = (begin + end) / 2;
//            int n1 = mid - 1, n2 = mid - begin + mid - 1;
        int n1 = begin, n2 = begin + 1;
        //[begin, mid) [mid, end)
        //[begin, n1] [, n2]
        while (n1 < mid) {
            if (s[n1] != s[n2]) {
                ++n1; n2 += 2;
                continue;
            }
            bool flag = true;
            for (int i = n1 - 1, j = n2 - 1; i >= begin; --i, --j) {
                if (s[i] != s[j]) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                if (end - n1 > memo[begin]) memo[begin] = std::max(memo[begin], aux(s, n1 + 1, end, 1));
                else break;
            }
            ++n1; n2 += 2;
        }
        if (~memo[begin]) return memo[begin] + cnt;
        else return (memo[begin] = 1) + cnt;
    }
public:
    int deleteString(string s) {
        memo.resize(s.size(), -1);
        return aux(s, 0, s.size(), 0);
    }
};

思路分析

当时比赛的时候丝毫没有想到DP,我对动态规划的确不太熟悉,就想着用一个贪心,先去掉长的重复串,再去掉短的,当时想着短的无论如何后面还可以再去掉,所以应该是比较优的。结果实现了一下,发现对于多个重复的串还是不行。贪心不行就想改成搜索,还是很机灵地加上了记忆化搜索,但是还是超时,没有办法必须考虑剪枝。剪枝的策略:如果后面的字符个数比当前的最大操作数大,再去搜索,这样可以解决的原因在于对于有多个重复字符的串,由于是从小串开始删除,所以会导致操作数很大,一旦搜索到大串的时候就会直接跳出。一共有O(n)种状态,每个状态都需要O(n)复杂度进行搜索,因此总的复杂度是O(n^2)的。

总体代码实现很优雅、紧凑, 我自己很满意。我觉得得益于在实现之前先对一个稍微复杂的串进行手动模拟,加上对于搜索、记忆化、剪枝这些技术逐渐熟稔,所以能够在有效的时间里面实现出一个sophisticated的代码。

当时遇到了三个bug:

  • 是在搜索的过程中保存的是前一个串和后一个串的末尾,在修改搜索方向后忘记末尾应该指向begin而让其指向了0
  • 在加上记忆化搜索后有点分不清楚状态的定义和函数返回值的定义,函数的返回值应该是对当前串[begin, end)cnt次搜索情况下进行搜索的最大删除方案,而记忆化搜索保存的是对当前串[begin, end)进行搜索的最大删除方案
  • 剪枝刚开始没有跳出循环,所以还是超时

看了一下题解,发现记忆化搜索和DP只不过是两种不同的思考方向。

周赛总结

优点

  • 思路灵活

缺点

  • 代码能力有待提高,对于记忆化搜索+剪枝这一套组合拳还是不够熟悉

改进方案

多做题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值