力扣306场周赛题解 | 青训营笔记

269 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第9天

6148. 矩阵中的局部最大值

给你一个大小为 n x n 的整数矩阵 grid 。

生成一个大小为 (n - 2) x (n - 2) 的整数矩阵  maxLocal ,并满足:

  • maxLocal[i][j] 等于 grid 中以 i + 1 行和 j + 1 列为中心的 3 x 3 矩阵中的 最大值 。

换句话说,我们希望找出 grid 中每个 3 x 3 矩阵中的最大值。

返回生成的矩阵。

image.png

输入:grid = [[9,9,8,1],[5,6,2,6],[8,2,6,4],[6,2,2,2]]
输出:[[9,9],[8,6]]
解释:原矩阵和生成的矩阵如上图所示。
注意,生成的矩阵中,每个值都对应 grid 中一个相接的 3 x 3 矩阵的最大值。

提示:

  • n == grid.length == grid[i].length
  • 3 <= n <= 100
  • 1 <= grid[i][j] <= 100

思路分析:签到题,根据100的数据量直接暴力即可。

class Solution {
    public:
        vector<vector<int>> largestLocal(vector<vector<int>>& grid) {
            vector<vector<int>> res;
            int n = grid.size();
            for (int i = 0; i < n - 2; i ++ ) {
                vector<int> t;
                for (int j = 0; j < n - 2; j ++ ) {
                    int num = 0;
                    for (int a = i; a < 3 + i; a ++ )
                        for (int b = j; b < 3 + j; b ++ )
                            num = max(num,grid[a][b]);
                    t.push_back(num);
                }
                res.push_back(t);
            }
            return res;
        }
    };

6149. 边积分最高的节点

给你一个有向图,图中有 n 个节点,节点编号从 0 到 n - 1 ,其中每个节点都 恰有一条 出边。

图由一个下标从 0 开始、长度为 n 的整数数组 edges 表示,其中 edges[i] 表示存在一条从节点 i 到节点 edges[i] 的 有向 边。

节点 i 的 边积分 定义为:所有存在一条指向节点 i 的边的节点的 编号 总和。

返回 边积分 最高的节点。如果多个节点的 边积分 相同,返回编号 最小 的那个。

 

示例 1:

image.png

输入:edges = [1,0,0,0,0,7,7,5]
输出:7
解释:
- 节点 1、2、3 和 4 都有指向节点 0 的边,节点 0 的边积分等于 1 + 2 + 3 + 4 = 10 。
- 节点 0 有一条指向节点 1 的边,节点 1 的边积分等于 0 。
- 节点 7 有一条指向节点 5 的边,节点 5 的边积分等于 7 。
- 节点 5 和 6 都有指向节点 7 的边,节点 7 的边积分等于 5 + 6 = 11 。
节点 7 的边积分最高,所以返回 7 。

提示:

  • n == edges.length
  • 2 <= n <= 105
  • 0 <= edges[i] < n
  • edges[i] != i

思路分析: 依据题意,要求我们找出每个点指向自己的权值,这里的权值就是点的数值。那我们可以直接开始搜索,然后只需要用一个数组存起来每个点指向自己的权值和,最后遍历一遍数组找出答案即可。由于每个点只搜到了一遍,所以时间复杂度为O(n).

class Solution {
    public:
        long long res[200010];
        bool st[200010];
        vector<int> e;
        void dfs(int u) {
            if (st[u]) return ;
            st[u] = 1;
            int t = e[u];
            if (t == -1) return;
            res[t] += u;
            if (!st[t]) dfs(t);
        }
        int edgeScore(vector<int>& edges) {
            e = edges;
            int n = e.size();
            for (int i = 0; i < n; i ++ ) {
                dfs(i);
            }
            long long ans = 0,root = 0;
            for (int i = 0; i < n; i ++ ) {
                if (res[i] > ans){
                    ans = res[i];
                    root = i;
                }
            }
            return root;
        }
    };

6150. 根据模式串构造最小数字

给你下标从 0 开始、长度为 n 的字符串 pattern ,它包含两种字符,'I' 表示 上升 ,'D' 表示 下降 。

你需要构造一个下标从 0 开始长度为 n + 1 的字符串,且它要满足以下条件:

  • num 包含数字 '1' 到 '9' ,其中每个数字 至多 使用一次。
  • 如果 pattern[i] == 'I' ,那么 num[i] < num[i + 1] 。
  • 如果 pattern[i] == 'D' ,那么 num[i] > num[i + 1] 。

请你返回满足上述条件字典序 最小 的字符串 **num

示例 1:

输入: pattern = "IIIDIDDD"
输出: "123549876"
解释: 下标 0124 处,我们需要使 num[i] < num[i+1] 。
下标 3567 处,我们需要使 num[i] > num[i+1] 。
一些可能的 num 的值为 "245639871""135749862""123849765""123549876" 是满足条件最小的数字。
注意,"123414321" 不是可行解因为数字 '1' 使用次数超过 1 次。

提示:

  • 1 <= pattern.length <= 8
  • pattern 只包含字符 'I' 和 'D' 。

思路分析: 依据题意,我们需要根据所给的字符串pattern求出满足要求的答案,这个题目好像又结论,但是看完数据量后发现不需要猜结论,题目的数据范围只有8,那我们直接构造全排列答案的计算量也才36w+,每次求出一个答案后再check一下看看是否满足情况,并且由于我们搜的顺序是按字典序大小,所以搜到的第一个答案就是最优解。

class Solution {
    public:
        bool st[10];
        string res,p;
        int n;
        bool flag = 0;
        bool check(string t) {
            for (int i = 0; i < n; i ++ ) {
                int a = t[i] - '0',b = t[i + 1] - '0';
                if (p[i] == 'I') {
                    if (a >= b) return false;
                }
                else {
                    if (a <= b) return false;
                }
            }
            return true;
        } 
        bool dfs(int u,string t){
            if (u > n) {
                if(check(t)) {
                    res = t;
                    return true;
                }
                return false;
            }
            
            for (int i = 1; i <= n + 1; i ++ ) {
                if (!st[i]) {
                    st[i] = 1;
                    if (dfs(u + 1,t + to_string(i))) return true;
                    st[i] = 0;
                }
            }
            
            return false;
        }
        string smallestNumber(string pattern) {
            p = pattern;
            n = p.size();
            dfs(0,"");
            return res;
        }
    };

6151. 统计特殊整数

如果一个正整数每一个数位都是 互不相同 的,我们称它是 特殊整数 。

给你一个  整数 n ,请你返回区间 **[1, n] 之间特殊整数的数目。

 

示例 1:

输入: n = 20
输出: 19
解释: 1 到 20 之间所有整数除了 11 以外都是特殊整数。所以总共有 19 个特殊整数。

示例 2:

输入: n = 5
输出: 5
解释: 1 到 5 所有整数都是特殊整数。

示例 3:

输入: n = 135
输出: 110
解释: 从 1 到 135 总共有 110 个整数是特殊整数。
不特殊的部分数字为:22 ,114 和 131 。

 

提示:

  • 1 <= n <= 2 * 10e9

思路分析:题意很好理解,就那么回事。这是一个数位dp的问题,在这里我们先把n有多少位取出来,然后根据位数来计算答案。把位数取出来后,我们可以把总数分为两大部分来考虑,第一部分是小于n位的位次的,第二部分和n同以位次的。他俩总和就是答案。

第一部分: 这一部分很好求,我们举个例子来解释:当n=23104时,n有5位,那我们可以先求出他只有1,2,3,4位的情况。

比如我们再求4位的情况,(这里有点容斥原理的内容)

    第一位可以选1-9,但是0不能选,因此能选9个。
    第二位可以选0-9,但是需要减去第一位选的数,因此只能选9个。
    第三位可以选0-9,但是需要减去前两位选的数,因此只能选8个。
    第四位可以选0-9,但是需要减去前两位选的数,因此只能选7个。
    所以sum = 9 * 9 * 8 * 7
    其他位次也一样...

第二部分: 这一部分就比较难了,还是用上面那个例子:当n=23104时,选五位的情况。 因为我们只能统计小于n的,所以我们每一次位次最多等于当前的值。比如第一位次时2,那么我此时只能选择1和2的情况,再第三次位,我只能选择0这种情况。

好那么我们就可以根据上面的分法来求出答案(画到纸上会发现这其实是一棵树)

    在第一位次时,我只能选择1和2,当选1时:那我们就回到上面那种计算的情况了,可求出当选1时,sum = 9 * 8 * 7 * 6,当选择2时,我们就直接跳转考虑第二位的情况。
    在第二位次时,我们就可以选择0,1,2,3这些情况,但是由于之前选过1了,所以我们需要用st数组记录选过了哪些数字。
    以此类推....

image.png

最后我们加上两部分的和,最后我们需要判断一下n是否也位合法情况,如果是就最后答案+1。 至此题目分析完毕,由于我们每次只选进了一个数而已,因此时间复杂度为O(n)

class Solution {
public:
    bool st[10];
    int countSpecialNumbers(int n) {
        vector<int> nums;
        while(n) {
            nums.push_back(n % 10);
            n /= 10;
        }
        reverse(nums.begin(),nums.end());
        int res = 0;
        for (int i = 1; i < nums.size(); i ++ ) {
            int t = 9;
            for (int j = 0,k = 9; j < i - 1; k -- ,j ++ )
                t *= k;
            res += t;
        }
        
        for (int i = 0; i < nums.size(); i ++ ) {
            for (int j = !i; j < nums[i]; j ++ ) {
                if (st[j]) continue;
                int t = 1;
                for (int k = 0,u = 9 - i; k < nums.size() - i - 1; k ++ ,u -- )
                    t *= u;
                res += t;
            }

            if(st[nums[i]]) break;
            st[nums[i]] = 1;
        }
        set<int> h(nums.begin(),nums.end());
        if (h.size() == nums.size()) res ++;
        return res;
    }
};