【前言】DFS + 回溯 第二篇
1 全排列(去重)
题目描述
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。说明:解集不能包含重复的子集。
样例
输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
笔记
- 加减乘除优先级 大于 所有位运算
- 例:1 2 2 2 3 3
每个数字可选次数(类似九宫格数字代表字母)
1 : 0 1
2 :0 1 2 3
3 :0 1 2
2 * 3 * 4 = 24种
一种一种枚举,所有数字枚举完就找到一个方案
代码
class Solution {
public:
vector<int> path;
vector<vector<int>> ans;
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(),nums.end());
dfs(nums,0);
return ans;
}
void dfs(vector<int>& nums,int count){
if(count == nums.size()){
ans.push_back(path);
return;
}
//计算当前数字的个数
int k=0;
while(count+k<nums.size() && nums[count+k] == nums[count]) k++;
//从0枚举到k,枚举每种数字放到哪个位置
for(int i=0;i<=k;i++){
dfs(nums,count+k);
path.push_back(nums[count]);
}
//恢复现场
for(int i=0;i<=k;i++) path.pop_back();
}
};
2 组合总和
题目描述
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。
解集不能包含重复的组合。
样例
输入: k = 3, n = 7
输出: [[1,2,4]]
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
笔记
1 依次枚举每个数,到哪个位置
2 dfs参数:枚举到了第几个数字count,开始枚举的位置start,当前所有数的和n
代码
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> combinationSum3(int k, int n) {
dfs(k,1,n);
return ans;
}
void dfs(int k,int start,int n){
if(!k){ //省变量,和模板是一样的,满足条件返回
if(!n) ans.push_back(path);
return;
}
for(int i=start;i<=10-k;i++){ //剩下起码有k个数 9-i+1>=k
path.push_back(i);
dfs(k-1,i+1,n-i);
path.pop_back();
}
}
};
3 N皇后 II
题目描述
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给定一个整数 n,返回 n 皇后不同的解决方案的数量。
样例
输入: 4
输出: 2
解释: 4 皇后问题存在如下两个不同的解法。
[
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],
["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
笔记
- 每行每列都只能一个皇后,相当于 行从1—n递增,对 n 个数进行全排列,依次枚举每一行皇后的位置
1 每一列只能有一个皇后,(全排列已满足此条件)
2 每条斜线只能有一个皇后,
代码
class Solution {
public:
int nn,sum=0;
vector<int> path;
vector<vector<int>> ans;
vector<int> used;
int totalNQueens(int n) {
nn=n;
used=vector<int>(n);
dfs(0);
return sum;
}
void dfs(int count){
if(count==nn){
sum++;
return;
}
for(int i=0;i<nn;i++){
if(used[i]==0){
bool flag=true;
//筛选满足题目要求的情况
for(int j=0;j<count;j++){
if(abs(i+1-path[j])==abs(j+1-count-1)) {
flag=false;
break;
}
}
if(flag){
used[i]=1;
path.push_back(i+1);
dfs(count+1);
path.pop_back();
used[i]=0;
}
}
}
}
};
4 解数独
题目描述
编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。
Note:
给定的数独序列只包含数字 1-9 和字符 ‘.’ 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。
样例
结果:
笔记
从前往后枚举每个空格 该填哪个数
状态: row[9][9] //i:每行 j:当前每个数字
col[9][9] //i:每列 j:当前每个数字
cell[3][3][9] //3×3 k:九宫格 共 9 个
代码
class Solution {
public:
bool row[9][9]={0},col[9][9]={0},cell[3][3][9]={0}; //维护数组
void solveSudoku(vector<vector<char>>& board) {
for(int i=0;i<9;i++){ //遍历board
for(int j=0;j<9;j++){
char c= board[i][j];
if(c!='.') {
int t=c-'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; //此处 x==9 和 y==0 两行不可对调,否则若行至右下角最后一个数,越界
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'; //result 结果数组维护
row[x][i]=col[y][i]=cell[x/3][y/3][i]=true; //辅助数组 维护
if(dfs(board,x,y+1)) return true;
row[x][i]=col[y][i]=cell[x/3][y/3][i]=false;//辅助数组 恢复
board[x][y]='.'; //result 结果数组恢复
}
}
return false;
}
};