【算法/题目】:递归、搜索训练

✨                                                 吾与春风皆过客,君携春水揽星河         🌏 

📃个人主页island1314

🔥个人专栏:算法训练

🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏  💞 💞 💞


目录

1. 找出所有子集的异或总和再求和

2.  N皇后

3. 有效的数独

4、解数独

5、单词搜索

6、黄金矿工

7、不同路径III


1. 找出所有子集的异或总和再求和

思路:

        假设存在[1,2,3]这个集合,那么开始的时候是空集合,画出其决策树

全局变量:sum和path

sum用于求异或和,path用来当进入某一层时,异或该数,

方法dfs : dfs(nums[],pos) 在pos那层

回溯:异或运算:消消乐(相同的数异或为0)

AC代码如下:

int path, sum;
void dfs(vector<int>& nums, int pos)
{
	sum += path;
	for (int i = pos; i < nums.size(); i++)
	{
		path ^= nums[i];
		dfs(nums, i + 1);
		path ^= nums[i]; //恢复现场
	}
}
int subsetXORSum(vector<int>& nums) {
	dfs(nums, 0);
	return sum;
}

2.  N皇后

思路:

深度优先遍历(DFS)

函数名:void dfs(int r): 深度优先遍历函数。参数r:从第r行开始放棋子,处理第r行。

递归结束判定:见代码,当 r == n的时候,说明应该处理第 n行了,也代表第 0~n-1行放好棋子,也就是整个棋盘放好了棋子,也就是得到了一种解,也就是递归结束。

第r行,第i列能不能放棋子:数组dg udl 分别表示:点对应的两个斜线以及列上是否有皇后。
dg[x + y] 表示 y行x列处,所在的对角线上有没有棋子,udg[n - x + y]表示 r行i列处,所在的反对角线上有没有棋子,cor[i]表示第i列上有没有棋子。如果 y行x列的对角线,反对角线上都没有棋子,即!col[x] && !dg[i + r] && !udg[n - i + r]为真,则代表 x行y列处可以放棋子。

AC代码如下:

vector<vector<string>> ret;
vector<string> path;
bool col[20], udg[20], dg[20];
int n;

void dfs(int pos) {
    if (pos == n) {
        ret.push_back(path);
        return;
    }
    int x = pos;
    for (int y = 0; y < n; y++) { // 尝试在这一行放皇后
        if (!col[y] && !dg[y - x + n] && !udg[y + x]) {
            col[y] = dg[y - x + n] = udg[y + x] = true;
            path[x][y] = 'Q';

            dfs(x + 1);
            path[x][y] = '.';
            col[y] = dg[y - x + n] = udg[y + x] = false;
        }
    }
}

vector<vector<string>> solveNQueens(int _n) {
    n = _n;
    path.resize(n);
    for (int i = 0; i < n; i++) {
        path[i].append(n, '.');
    }
    dfs(0);
    return ret;
}
   

3. 有效的数独

 

思路:

创建二维数组 rows 和 col 分别记录数独的每一行和每一列中的每个数字的出现次数

创建三维数组 grid 记录数独的每一个小九宫格中的每个数字的出现次数

        其中rows[i][num]、columns[j][num] 和 gird[i / 3] [j / 3][num] 分别表示数独的第 i 行第 j 列的单元格所在的行、列和小九宫格中,数字 num + 1 出现的次数,其中 0≤ num <9,对应的数字 num+1 满足 1≤num+1≤9。

AC代码如下:

bool col[9][10], row[9][10];
bool grid[3][3][10];
bool isValidSudoku(vector<vector<char>>& board) {
	for (int i = 0; i < 9; i++) {
		for (int j = 0; j < 9; j++) {
			if (board[i][j] != '.') {
				int num = board[i][j] - '0';
				if (row[i][num] || col[j][num] || grid[i / 3][j / 3][num])
					return false;
				row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = true;
			}
		}
	}
	return true;
}

4. 解数独

思路:

和上题类似的是,我们同样用

   创建二维数组 rows 和 col 分别记录数独的每一行和每一列中的每个数字的出现次数

   创建三维数组 grid 记录数独的每一个小九宫格中的每个数字的出现次数

我们首先对整个数独数组进行遍历,当我们遍历到第 i 行第 j 列的位置:

  • 如果该位置是一个空白格,那么我们将其加入一个用来存储空白格位置的列表中,方便后续的递归操作;

  • 如果该位置是一个数字 num,那么我们需要将 row[ i ][ num ],col[ j ][num以及 block[ ⌊i/3⌋ ][ ⌊j/3⌋ ][num] 均置为 True。

当我们结束了遍历过程之后,就可以开始递归枚举。当递归到第 i 行第 j 列的位置时,我们枚举填入的数字 num。根据题目的要求,数字 num 不能和当前行、列、九宫格中已经填入的数字相同,因此 row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = false;

当我们填入了数字 num 之后,我们要将上述的三个值都置为 True,并且继续对下一个空白格位置进行递归。在回溯到当前递归层时,我们还要将上述的三个值重新置为 False。

AC代码如下:

bool row[9][10], col[9][10];//储存每一行每一列存在的数字 
bool grid[3][3][10]; //储存每一个 3*3宫存在的数字

bool dfs(vector<vector<char>>& board) {
	for (int i = 0; i < 9; i++) {
		for (int j = 0; j < 9; j++) {
			if (board[i][j] == '.') {
				//填数
				for (int num = 1; num <= 9; num++) {
					if (!row[i][num] && !col[j][num] && !grid[i / 3][j / 3][num]) {
						board[i][j] = '0' + num;
						row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = true;
						if (dfs(board) == true) return true;

						//恢复现场
						board[i][j] = '.';
						row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = false;
					}
				}
				// 当前格子 1- 9 都不能填,那么就只能返回到上一个格子进行修改
				return false;
			}
		}
	}
	return true;
}

void solveSudoku(vector<vector<char>>& board) {
	//初始化
	for (int i = 0; i < 9; i++) {
		for (int j = 0; j < 9; j++) {
			if (board[i][j] != '.') {
				int num = board[i][j] - '0';
				row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = true;
			}
		}
	}
	dfs(board);
}

5. 单词搜索

思路:

设函数 dfs(board,words,x,y,pos) 表示判断以网格的 (x,y)位置出发,能否搜索到单词 words[pos..],其中 words[pos..] 表示字符串 word 从第 pos 个字符开始的后缀子串。如果能搜索到,则返回 true,反之返回 false。函数 dfs(board,words,x,y,pos)的执行步骤如下:

  • 如果 board[ x ][ y ] = words[pos],当前字符不匹配,直接返回 false。
  • 如果当前已经访问到字符串的末尾,且对应字符依然匹配,此时直接返回 true。
  • 否则,遍历当前位置的所有相邻位置。如果从某个相邻位置出发,能够搜索到子串 word[pos+1..],则返回 true,否则返回 false。

这样,我们对每一个位置 (x,y)都调用函数 dfs(board,words,x,y,pos)进行检查:只要有一处返回 true,就说明网格中能够找到相应的单词,否则说明不能找到。

为了防止重复遍历相同的位置,需要额外维护一个与 board 等大的 st 数组,用于标识每个位置是否被访问过。每次遍历相邻位置时,需要跳过已经被访问的位置。

注意:

if (dfs(board, word, i, j, 0)) return true;,而不是return dfs(board, word, i, j, 0);

AC代码如下:

int dir[4][2] = {
		{1,0},{0,1},{-1,0},{0,-1}
};
int n, m;
bool st[505][505];

bool dfs(vector<vector<char>>& board, string word, int x, int y, int pos) {
	if (pos == word.size()) return true;
	//向量的方式,定义上下左右四个位置

	for (int i = 0; i < 4; i++) {
		int dx = x + dir[i][0], dy = y + dir[i][1];
		if (dx >= 0 && dx < n && dy >= 0 && dy < m && board[dx][dy] == word[pos] && !st[dx][dy]) {
			st[dx][dy] = true;
			if (dfs(board, word, dx, dy, pos + 1)) return true;
			st[dx][dy] = false;
		}
	}
	return false;
}

bool exist(vector<vector<char>>& board, string word) {
	n = board.size(), m = board[0].size();
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (board[i][j] == word[0]) {
				st[i][j] = true;
				if (dfs(board, word, i, j, 1)) return true;
				st[i][j] = false;
			}
		}
	}
	return false;
}

6. 黄金矿工

思路:

该题与上题解题步骤基本类似,只不过该题需要多加一个参数sum,来记录每条的和,然后求出最大值即可。

AC代码如下:

int dir[4][2] = {
    {1,0},{0,1},{-1,0},{0,-1}
};
int maxi = 0;
int n, m;
bool st[505][505];

void dfs(vector<vector<int>>& grid, int x, int y, int sum) {
    maxi = max(maxi, sum);
    for (int i = 0; i < 4; i++) {
        int dx = x + dir[i][0], dy = y + dir[i][1];
        if (dx >= 0 && dx < n && dy >= 0 && dy < m && grid[dx][dy] > 0 && !st[dx][dy]) {
            st[dx][dy] = true;
            dfs(grid, dx, dy, sum + grid[dx][dy]);
            st[dx][dy] = false;

        }
    }
}

int getMaximumGold(vector<vector<int>>& grid) {
    n = grid.size(), m = grid[0].size();
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] != 0) {
                st[i][j] = true;
                dfs(grid, i, j, grid[i][j]);
                st[i][j] = false;
            }
        }
    }
    return maxi;
}

7. 不同路径III

思路:

该题我们选择dfs的方法,主要步骤和 5、6题过程类似,但是在进行dfs之前,我们先需要做一些初始化的步骤,比如找到起始位置,和记录应该需要走的总步数

AC代码如下:

bool st[25][25];
int dir[4][2] = {
    {1,0},{0,1},{-1,0},{0,-1}
};
int ret, step; //统计走的方法,和需要走的总步数
int n, m;

void dfs(vector<vector<int>>& grid, int x, int y, int cnt) {
    if (grid[x][y] == 2) { //走到终止位置
        if (cnt == step) ret++; //看是否走完所有路程
        return;
    }
    for (int i = 0; i < 4; i++) {
        int dx = x + dir[i][0], dy = y + dir[i][1];
        if (dx >= 0 && dx < n && dy >= 0 && dy < m && grid[dx][dy] != -1 && !st[dx][dy]) {
            st[dx][dy] = true;
            dfs(grid, dx, dy, cnt + 1);
            st[dx][dy] = false;
        }
    }
}

int uniquePathsIII(vector<vector<int>>& grid) {
    n = grid.size(), m = grid[0].size();
    
    int bx = 0, by = 0; //记录起始位置
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 0) step++; // 统计需要走的总步数
            else if (grid[i][j] == 1) bx = i, by = j;
        }
    }
    step += 2; //还需要加上起始位置和终止位置走的步数
    st[bx][by] = true;
    dfs(grid, bx, by, 1);
    return ret;
}

8. 体操队形

思路:

AC代码如下:

#include <iostream>
#include <algorithm>
using namespace std;
int a[14];
bool vis[14];
int ans = 0, n;

void dfs(int pos){
	if (pos == n + 1) {
		ans++;
		return;
	}
	for (int i = 1; i <= n; i++){
		if (vis[i]) continue;  //剪枝已经放过了
		if (vis[a[i]]) return; //当前要放的元素要在其对应元素之前
		vis[i] = true; //放上第 i 号队员
		dfs(pos + 1);
		vis[i] = false; //恢复现场
	}
}

int main(){
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> a[i];
	dfs(1);
    cout << ans << endl;
	return 0;
}


  

  • 63
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 95
    评论
评论 95
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值