LeetCode究极班系列(36-40)

36. 有效的数独

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

输入:
[
[“5”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: true

输入:
[
[“8”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: false
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。

说明:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
给定数独序列只包含数字 1-9 和字符 ‘.’ 。
给定数独永远是 9x9 形式的。

算法描述

按照有效性的定义直接模拟就好了

模拟

class Solution {
public:
    bool st[9]; 
    bool isValidSudoku(vector<vector<char>>& board) {
        for(int i=0;i<9;i++){
            memset(st,0,sizeof st);
            for(int j=0;j<9;j++){
                if(board[i][j]!='.'){
                    int t=board[i][j]-'1';
                    if(st[t]) return false;
                    st[t]=true;
                }
            }
        }

        for(int i=0;i<9;i++){
            memset(st,0,sizeof st);
            for(int j=0;j<9;j++){
                if(board[j][i]!='.'){
                    int t=board[j][i]-'1';
                    if(st[t]) return false;
                    st[t]=true;
                }
            }
        }

        for(int i=0;i<9;i+=3){
            for(int j=0;j<9;j+=3){
                memset(st,0,sizeof st);
                for(int x=0;x<3;x++){
                    for(int y=0;y<3;y++){
                        if(board[i+x][j+y]!='.'){
                            int t=board[i+x][j+y]-'1';
                            if(st[t]) return false;
                            st[t]=true;
                        }
                    }
                }
            }
        }
        return true;


    }
};

37. 解数独

编写一个程序,通过填充空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。

在这里插入图片描述
在这里插入图片描述

提示:
给定的数独序列只包含数字 1-9 和字符 ‘.’ 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。

算法描述

开三个数组表示行列以及小方格内的9个元素是否被使用
然后枚举空格的所有可能性 直接回溯

暴力回溯

class Solution {
public:
    bool row[9][9],col[9][9],cell[3][3][9];
    void solveSudoku(vector<vector<char>>& board) {
        memset(row,0,sizeof row);
        memset(col,0,sizeof col);
        memset(cell,0,sizeof cell);


        for(int i=0;i<9;i++)
            for(int j=0;j<9;j++){
                if(board[i][j]!='.'){
                    int t=board[i][j]-'1';
                    row[i][t]=col[j][t]=cell[i/3][j/3][t]=true;
                }
            }

        dfs(board,0,0);
    }

    bool dfs(vector<vector<char>>& board,int x,int y){
        if(y==9) x++,y=0;
        if(x==9) return true;

        if(board[x][y]!='.') return dfs(board,x,y+1);
        for(int i=0;i<9;i++){
            if(!row[x][i] && !col[y][i] && !cell[x/3][y/3][i]){
                board[x][y]=i+'1';
                row[x][i]=col[y][i]=cell[x/3][y/3][i]=true;
                if(dfs(board,x,y+1)) return true;
                board[x][y]='.';
                 row[x][i]=col[y][i]=cell[x/3][y/3][i]=false;
            }
        }
        return false;

    }
};

38. 外观数列

给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 n 项。
注意:整数序列中的每一项将表示为一个字符串。
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:

  1. 1
    
  2. 11
    
  3. 21
    
  4. 1211
    
  5. 111221
    

第一项是数字 1
描述前一项,这个数是 1 即 “一个 1 ”,记作 11
描述前一项,这个数是 11 即 “两个 1 ” ,记作 21
描述前一项,这个数是 21 即 “一个 2 一个 1 ” ,记作 1211
描述前一项,这个数是 1211 即 “一个 1 一个 2 两个 1 ” ,记作 111221

输入: 4
输出: “1211”
解释:当 n = 3 时,序列是 “21”,其中我们有 “2” 和 “1” 两组,“2” 可以读作 “12”,也就是出现频次 = 1 而 值 = 2;类似 “1” 可以读作 “11”。所以答案是 “12” 和 “11” 组合在一起,也就是 “1211”。

算法描述

每一次的字符串都是对上一次字符串的计算得出的 统计每个字符的个数 然后加到当前字符串里面

双指针

class Solution {
public:
    string countAndSay(int n) {
        string s="1";
        for(int i=0;i<n-1;i++){
            string t;
            for(int j=0;j<s.size();){
                int k=j+1;
                while(k<s.size() && s[k]==s[j]) k++;
                t+=to_string(k-j)+s[j];
                j=k;
            }
            s=t;
        }
        return s;
    }
};

39. 组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]

算法描述

回溯法的典型应用场景

dfs(朴素思想)

class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<int> path;
        dfs(candidates,path,target,0);
        return res;
    }
    void dfs(vector<int>& a,vector<int>& path,int target,int idx){
        if(target==0){
            res.push_back(path);
            return;
        }
        for(int i=idx;i<a.size();i++){
            if(target<a[i]) continue;
            path.push_back(a[i]);
            dfs(a,path,target-a[i],i);
            path.pop_back();
        }    
    }
};

dfs(多重背包思想)

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    vector<vector<int>> combinationSum(vector<int>& c, int target) {
        dfs(c,0,target);
        return res;
    }
    void dfs(vector<int>& c,int u,int target){ //u是当前枚举到第几个数了
        if(target==0){
            res.push_back(path);
            return;
        }
        if(u==c.size()) return;
        for(int i=0;c[u]*i<=target;i++){
            dfs(c,u+1,target-c[u]*i);
            path.push_back(c[u]);//因为是u选0个开始的 所以后面再push_back
        }
        for(int i=0;c[u]*i<=target;i++){
            path.pop_back();
        }
    }
};

40. 组合总和 II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]

算法描述

照搬上一题了 就加了排序和暴力去重

暴力dfs

class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(),candidates.end());
        vector<int> path;
        dfs(candidates,path,target,0);
        return res;
    }
    void dfs(vector<int>& a,vector<int>& path,int target,int idx){
        if(target<=0){
            if(target==0 && count(res.begin(),res.end(),path)==0) res.push_back(path);
            return;
        }
        for(int i=idx;i<a.size();i++){
            path.push_back(a[i]);
            dfs(a,path,target-a[i],i+1);
            path.pop_back();
        }    
    }
};

算发描述

相比于上面一个用find函数暴力查找的方法 这个使用了一个布尔数组和一句判断解决去重
我们看这句代码 if(i>0 && a[i]==a[i-1] && !st[i-1]) continue; 字面意思就是当前元素和上一个元素相同 并且上一个元素未被使用 就跳过 原理来自于逻辑推导或者是搜索树的剪枝

优化dfs

class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<bool> st(candidates.size());
        vector<int> path;
        sort(candidates.begin(),candidates.end());
        dfs(st,candidates,path,target,0);
        return res;
    }
    void dfs(vector<bool>& st,vector<int>& a,vector<int>& path,int target,int idx){
        if(target==0){
            res.push_back(path);
            return;
        }
        for(int i=idx;i<a.size();i++){
            if(st[i] || target<a[i]) continue;
            if(i>0 && a[i]==a[i-1] && !st[i-1]) continue;
            path.push_back(a[i]);
            st[i]=true;
            dfs(st,a,path,target-a[i],i+1);
            path.pop_back();
            st[i]=false;
        }    
    }
};

算法描述

每个物品限用一次 又可能存在重复元素 就是在01背包的基础上加了每个物品限用n次的要求 就是多重背包问题
那么这一题和上一题的差别就是多了一个每个物品限用n次的限制 只要加上这个限制上一题的代码就可以直接跑通了
这里一个简单的思路就是 先对所给数组排序 然后就是双指针得到个数的常规思路 然后枚举元素的时候除了不能超出背包体积 个数也不能超

dfs(多重背包思想)

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    vector<vector<int>> combinationSum2(vector<int>& c, int target){
        sort(c.begin(),c.end());
        dfs(c,0,target);
        return res;
    }
    void dfs(vector<int>& c,int u,int target){
        if(target==0){
            res.push_back(path);
            return;
        }
        if(u==c.size()) return;
        int k=u+1;
        while(k<c.size() && c[k]==c[u]) k++;
        int cnt=k-u;
        for(int i=0;i*c[u]<=target && i<=cnt;i++){
            dfs(c,k,target-i*c[u]);
            path.push_back(c[u]);
        }
        for(int i=0;i*c[u]<=target && i<=cnt;i++){
            path.pop_back();
        }
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值