1. 程式人生 > 其它 >寒假CS每日打卡 Feb.2nd

寒假CS每日打卡 Feb.2nd

技術標籤:2021寒假每日打卡演算法C++寒假學習LeetCodeAcwing


演算法部分

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分析法,做一下總結