寒假CS每日打卡 Feb.2nd


算法部分

1.Acwing 入门组每日一题
题目:数独检查
  数独是一种流行的单人游戏。目标是用数字填充9x9矩阵,使每列,每行和所有9个非重叠的3x3子矩阵包含从1到9的所有数字。每个9x9矩阵在游戏开始时都会有部分数字已经给出,通常有一个独特的解决方案。
sadf
we
  给定完成的N2∗N2数独矩阵,你的任务是确定它是否是有效的解决方案。有效的解决方案必须满足以下条件:
每行包含从1到N2的每个数字,每个数字一次。
每列包含从1到N2的每个数字,每个数字一次。
将N2∗N2矩阵划分为N2个非重叠N∗N子矩阵。 每个子矩阵包含从1到N2的每个数字,每个数字一次。
  你无需担心问题的唯一性,只需检查给定矩阵是否是有效的解决方案即可。
输入格式
第一行包含整数T,表示共有T组测试数据。
每组数据第一行包含整数N。
接下来N2行,每行包含N2个数字(均不超过1000),用来描述完整的数独矩阵。
输出格式
每组数据输出一个结果,每个结果占一行。
结果表示为“Case #x: y”,其中x是组别编号(从1开始),如果给定矩阵是有效方案则y是Yes,否则y是No。
数据范围
1≤T≤100,
3≤N≤6
输入样例:
3
3
5 3 4 6 7 8 9 1 2
6 7 2 1 9 5 3 4 8
1 9 8 3 4 2 5 6 7
8 5 9 7 6 1 4 2 3
4 2 6 8 5 3 7 9 1
7 1 3 9 2 4 8 5 6
9 6 1 5 3 7 2 8 4
2 8 7 4 1 9 6 3 5
3 4 5 2 8 6 1 7 9
3
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
3
5 3 4 6 7 8 9 1 2
6 7 2 1 9 5 3 4 8
1 9 8 3 4 2 5 6 7
8 5 9 7 6 1 4 2 3
4 2 6 8 999 3 7 9 1
7 1 3 9 2 4 8 5 6
9 6 1 5 3 7 2 8 4
2 8 7 4 1 9 6 3 5
3 4 5 2 8 6 1 7 9
输出样例:
Case #1: Yes
Case #2: No
Case #3: No

题解:
  依次检查每行、每列、每个小九宫格是否满足条件,使用 bool 数组 记录元素的出现情况。
代码:

#include <iostream>
#include <cstring>

using namespace std;

const int MAXN = 50;
bool vis[MAXN][MAXN];
int g[MAXN][MAXN];

void check(int r, int c, bool &op, int cnt, int size){
    
    
    for(int i = r; i < r + size; i ++)
        for(int j = c; j < c + size; j ++){
    
    
            if(vis[cnt][g[i][j]])
                op = false;
            vis[cnt][g[i][j]] = true;   
        }
}

int main(){
    
    
    int n;

    cin >> n;
    for(int i = 1; i <= n; i ++){
    
    
        bool op = true;
        int size;

        cin >> size;
        for(int i = 0; i < size * size; i ++)
            for(int j = 0; j < size *size; j ++){
    
    
                cin >> g[i][j];
            if(g[i][j] < 1 || g[i][j] > size * size)
                op = false;             
            }

        //检查行
        memset(vis, false, sizeof(vis));
        for(int i = 0; i < size * size && op; i ++)
            for(int j = 0; j < size * size && op; j ++){
    
    
                if(vis[i][g[i][j]])
                    op = false;
                vis[i][g[i][j]] = true;
            }
        //检查列
        memset(vis, false, sizeof(vis));
        for(int i = 0; i < size * size && op; i ++)
            for(int j = 0; j < size * size && op; j ++){
    
    
                if(vis[i][g[j][i]])
                    op = false;
                vis[i][g[j][i]] = true;
            }

        //检查 size * size 个子矩阵
        int cnt = 0;
        memset(vis, false, sizeof(vis)); 
        for(int i = 0; i < size && op; i ++)
            for(int j = 0; j < size && op; j ++)
                check(i * size, j * size, op, cnt, size), cnt ++;

        if(op)
            cout << "Case #" << i << ": Yes" << endl;
        else
            cout << "Case #" << i << ": No" << endl;
    }

    return 0;
}

2.Acwing 提高组每日一题
题目:数独简单版
  数独是一种传统益智游戏,你需要把一个 9×9 的数独补充完整,使得图中每行、每列、每个 3×3 的九宫格内数字 1∼9 均恰好出现一次。请编写一个程序填写数独。
输入格式
输入共 9 行,每行包含一个长度为 9 的字符串,用来表示数独矩阵。
其中的每个字符都是 1∼9 或 .(表示尚未填充)。
输出格式
输出补全后的数独矩阵。
数据保证有唯一解。
输入样例:
.2738…1.
.1…6735
…29
3.5692.8.

.6.1745.3
64…
9518…7.
.8…6534.
输出样例:
527389416
819426735
436751829
375692184
194538267
268174593
643217958
951843672
782965341

题解:
  dfs深搜求解,注意剪枝,也就是考虑这一行、这一列以及小九宫格数字的出现情况,需要数组记录。此外,把二维坐标映射到一维,使用公式 r * 边长 + c 即可将二维坐标映射到一维,而且没有冲突。

#include <iostream>

using namespace std;

const int MAXN = 10;
char g[MAXN][MAXN];
bool hang[MAXN][MAXN], lie[MAXN][MAXN], sub[MAXN][MAXN];

void dfs(int le){
    
    
	//递归出口
    if(le == 81){
    
    
        for(int i = 0; i < 9; i ++)
            cout << g[i] << endl;
        return ;
    }
	//从一维坐标得到行和列
    int r = le / 9;
    int c = le % 9;
	//已经摆好了,则进行下一层递归
    if(g[r][c] != '.'){
    
    
        dfs(le + 1);
        return; 
    }
	//有1 - 9 种放法
    for(int i = 1; i <= 9; i ++){
    
    
        if(hang[r][i] || lie[c][i] || sub[(r / 3) * 3 + c / 3][i])
            continue;
        g[r][c] = i + '0';
        hang[r][i] = true;
        lie[c][i] = true;
        sub[(r / 3) * 3 + c / 3][i] = true;
        dfs(le + 1);
        //回溯之后恢复现场
        hang[r][i] = false;
        lie[c][i] = false;
        sub[(r / 3) * 3 + c / 3][i] = false;    
        g[r][c] = '.';
    }
}

int main(){
    
    
    for(int i = 0; i < 9; i ++)
        cin >> g[i];
	//预处理 行、列小九宫格信息
    for(int i = 0; i < 9; i ++)
        for(int j = 0; j < 9; j ++){
    
    
            if(g[i][j] != '.'){
    
    
                hang[i][g[i][j] - '0'] = true;
                lie[j][g[i][j] - '0'] = true;
                sub[(i / 3) * 3 + j / 3][g[i][j] - '0'] = true; 
            }
        }

    dfs(0);
    return 0;
}

3.LeetCode 每日一题
题目:替换后的最长重复字符
  给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。
注意:字符串长度 和 k 不会超过 104。

示例 1:
输入:s = “ABAB”, k = 2
输出:4
解释:用两个’A’替换为两个’B’,反之亦然。
示例 2:
输入:s = “AABABBA”, k = 1
输出:4
解释:
将中间的一个’A’替换为’B’,字符串变为 “AABBBBA”。
子串 “BBBB” 有最长重复字母, 答案为 4。
题解:
  将题目抽象为维护一个区间,当满足条件就扩大这个区间,否则缩小区间,在这个头尾扩展的过程中维护最大值,一个区间满足能满足转变为同一字符的条件是 区间长度 <= K + 区间内最大的同一字符出现次数,因为只需要把其他字符都转变为那个出现次数最多的字符即可。

class Solution {
    
    
public:
    int characterReplacement(string s, int k) {
    
    
        int res = 0, le = 0, ri = 0, ans = 0;
        vector<int> cnt(26, 0);

        while(ri < s.length()){
    
    
            cnt[s[ri] - 'A'] ++;
            //更新历史最大单字符出现次数
            res = max(res, cnt[s[ri] - 'A']);
			//判断是否满足条件
            if(ri - le + 1 - k > res){
    
    
            //不满足,则需要移动头指针,同时将头指针对应的字符出现次数 -1
                cnt[s[le] - 'A'] --;
                ++ le;
            }
            ++ ri;
        }
        return ri - le;
    }
};

4.春招冲刺 - 从前序与中序遍历序列构造二叉树
题目:
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
   3
  /   \
  9   20
 /    \
15     7

题解:
  经典的数据结构题,做完本题可以尝试根据后序和中序遍历构造二叉树。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    
    
public:
	//key: value of node | value : index in inorder traversal
    unordered_map<int, int> map;

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
    
    
        int cur = 0;
        for(int i = 0; i < inorder.size(); i ++)
            map[inorder[i]] = i;
        return dfs(preorder, cur, inorder, 0, preorder.size() - 1);
    }

    TreeNode *dfs(vector<int> &pre, int &cur, vector<int> &in, int le, int ri){
    
    
        if(le > ri)
            return nullptr;
        //使用map能提高效率,mid为中序遍历对应的下标
        int mid = map[pre[cur]];
        // for(int i = le; i <= ri; i ++)
        //     if(pre[cur] == in[i]){
    
    
        //         mid = i;
        //         break;
        //     }
        TreeNode *root = new TreeNode(pre[cur]);
        ++ cur;
        //递归求左子树和右子树
        root -> left = dfs(pre, cur, in, le, mid - 1);
        root -> right = dfs(pre, cur, in, mid + 1, ri);
        return root;
    }
};

5.春招冲刺 - 阶乘后的零
题目:
给定一个整数 n,返回 n! 结果尾数中零的数量。
示例 1:
输入: 3
输出: 0
解释: 3! = 6, 尾数中没有零。

示例 2:
输入: 5
输出: 1
解释: 5! = 120, 尾数中有 1 个零.
说明: 你算法的时间复杂度应为 O(log n) 。

题解:
  细细品下面的代码,作为校招面试题也不错哦 ><

class Solution {
    
    
public:
    int trailingZeroes(int n) {
    
    
        int ans = 0;
        for(int i = 5; i <= n; i *= 5){
    
    
            ans += n /i;
        }
        return ans;
    }
};

书籍部分

图解TCP|IP 第2章 ✔
MYSQL必知必会 第9 - 12章 ✔


PS.

  1. 明天看一下markdown语法
  2. 有时间看一下闫氏dp分析法,做一下总结

猜你喜欢

转载自blog.csdn.net/Raymond_YP/article/details/113573678