深度优先搜索DFS深搜综合2(1832. 八皇后问题、1833. 八皇后、1955. 算24点、1956. 算24点(2)、1966. 人造星空、1914. 小H回家)

 题单地址:题单中心-东方博宜OJ

1832. 八皇后问题

问题描述

在国际象棋棋盘上放置八个皇后,要求每两个皇后之间不能直接吃掉对方。

输入

无输入。

输出

按给定顺序和格式输出所有八皇后问题的解(见Sample Output)。

样例

输入

输出

No. 1
1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 1
0 1 0 0 0 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 0 1 0 0
0 0 1 0 0 0 0 0
No. 2
1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 1
0 1 0 0 0 0 0 0
0 0 0 0 1 0 0 0
0 0 1 0 0 0 0 0
No. 3
1 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 1
0 0 1 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0
0 1 0 0 0 0 0 0
0 0 0 0 1 0 0 0
No. 4
1 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 1
0 0 0 0 0 1 0 0
0 0 1 0 0 0 0 0
0 0 0 0 0 0 1 0
0 1 0 0 0 0 0 0
0 0 0 1 0 0 0 0
No. 5
0 0 0 0 0 1 0 0
1 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0
0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 1
0 0 1 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0
...以下省略

解析:使用深度优先搜索确定每一行的皇后位置,使用数组 v 记录每一行皇后所能攻击到的位置,分别是同一列,右上到左下,左上到右下。使用一个数组 q 记录每个方案八个皇后的位置。

#include 
using namespace std;

int n, q[15], sum, a[100][100];
bool v[3][100]; 

void dfs(int k){ //k代表行 
	if(k == 9){ //全部格子填写完毕
		sum++; 
		for(int i = 1; i <= 8; i++)a[sum][i] = q[i];
		return ;
	}	
	for(int i = 1; i <= 8; i++){ //i代表列 
		if(v[0][i] == 0 && v[1][i + k] == 0 && v[2][k - i + 8] == 0){
			v[0][i] = 1; //代表列 
			v[1][i + k] = 1; //代表右上到左下 
			v[2][k - i + 8] = 1; //代表左上到右下 
			q[k] = i; //记录当前行的列数 
			dfs(k + 1); //找下一行 
			v[0][i] = 0; //回溯 
			v[1][i + k] = 0;
			v[2][k - i + 8] = 0;
		}
	}
}

int main() {
	dfs(1);
	for(int i = 1; i <= sum; i++){
		cout << "No. " << i << endl;
		for(int j = 1; j <= 8; j++){
			for(int k = 1; k <= 8; k++){
				if(a[i][k] == j)cout << 1 << ' ';
				else cout << 0 << ' '; 
			}
			cout << endl;
		}
	}
	return 0;
}

1833. 八皇后

问题描述

会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将 8 个皇后放在棋盘上(有 8×8 个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。

对于某个满足要求的 8 皇后的摆放方法,定义一个皇后串 a 与之对应,即 a=b1b2…b8,其中 bi​ 为相应摆法中第 i 行皇后所处的列数。已经知道 8 皇后问题一共有 92 组解(即 92 个不同的皇后串)。

给出一个数 b ,要求输出第 b 个串。串的比较是这样的:皇后串 x 置于皇后串 y 之前,当且仅当将 x 视为整数时比 y 小。

输入

第 1 行是测试数据的组数 n ,后面跟着 n 行输入。

每组测试数据占 1 行,包括一个正整数 b (1≤b≤921≤b≤92)。

输出

输出有 n 行,每行输出对应一个输入。输出应是一个正整数,是对应于 b 的皇后串。

样例

输入

2

1

92

输出

15863724

84136275

解析:使用深度优先搜索确定每一行的皇后位置,使用数组 v 记录每一行皇后所能攻击到的位置,分别是同一列,右上到左下,左上到右下。使用一个数组 q 记录每个方案八个皇后的位置。

#include 
using namespace std;

int n, q[15], sum, a[100][100], d;
bool v[3][100]; 

void dfs(int k){ //k代表行 
	if(k == 9){ //全部格子填写完毕
		sum++; 
		for(int i = 1; i <= 8; i++)a[sum][i] = q[i];
		return ;
	}	
	for(int i = 1; i <= 8; i++){ //i代表列 
		if(v[0][i] == 0 && v[1][i + k] == 0 && v[2][k - i + 8] == 0){
			v[0][i] = 1; //代表列 
			v[1][i + k] = 1; //代表右上到左下 
			v[2][k - i + 8] = 1; //代表左上到右下 
			q[k] = i; //记录当前行的列数 
			dfs(k + 1); //找下一行 
			v[0][i] = 0; //回溯 
			v[1][i + k] = 0;
			v[2][k - i + 8] = 0;
		}
	}
}

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

1955. 算24点

问题描述

给出 n 组 4 个整数,请问有多少组整数,在不改变顺序,且不加入括号的情况下,可以通过 + − × 三种运算,得到 24 。

比如 1 2 3 4 四个数,可以通过如下的方法得到24:1×2×3×4=24。

而 20 30 40 50 四个数,在不改变顺序、不添加括号的情况下,通过 + − × 三种运算是无法获得 24 的。

输入

第1行有一个整数 n;(2 ≤ n ≤ 100 )

接下来 n 行,每行有 4 个整数 ai ;( 1 ≤ ai​ ≤ 100 )

输出

输出一个整数,代表有几组数能够通过题目的规则计算得到 24 。

样例

输入

4

1 2 3 4

10 20 30 40

50 60 70 80

9 1 8 2

输出

2

解析:全排列做法,因为有乘法运算,并且每个数字顺序不变,所以先枚举运算符号,然后再计算是否能得到24点。

#include 
using namespace std;

int n, a[4], f = 0, ans = 0;
string dir = "+-*";

void com(string s){
	int b[10];
	for(int i = 0; i < 4; i++) b[i] = a[i];
	for(int i = 0; i < s.size(); i++){ // 先算乘 
		if(s[i] == '-'){
			b[i+1] = -b[i+1];
		} 
		else if(s[i] == '*'){
			b[i+1] *= b[i];
			b[i] = 0;
		}
	}
	int sum = 0;
	for(int i = 0; i < 4; i++) sum += b[i];
	if(sum == 24) f = 1;
}

void dfs(string s){
	if(s.size() == 3){
		com(s);
		return ;
	}
	for(int i = 0; i < dir.size(); i++){
		dfs(s + dir[i]);
	}
}

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

1956. 算24点(2)

问题描述

给出 n 个整数,请问这 n 个数字在不改变顺序且不加入括号的情况下,仅通过 +−× 三种运算符,有多少种运算能得到 24 。

比如:4 10 2 4 8,有如下 3 种运算能够得到24 。

4+10-2+4+8=24
4-10-2+4*8=24
4*10-2*4-8=24

输入

第 1 行有一个整数 n ;(2 ≤ n ≤ 10)

第 2 行有 n 个整数 ai​ ;(1 ≤ ai​ ≤ 20)

输出

一个整数,代表能计算出 24 点的方案数;

样例

输入

5

4 10 2 4 8

输出

3

解析:全排列做法,因为有乘法运算,并且每个数字顺序不变,所以先枚举运算符号,然后再计算是否能得到24点,如果是记录方案。

#include 
using namespace std;

int n, a[10], f = 0, ans = 0;
string dir = "+-*";

void com(string s){
	int b[10];
	for(int i = 0; i < n; i++) b[i] = a[i];
	for(int i = 0; i < s.size(); i++){ // 先算乘 
		if(s[i] == '-'){
			b[i+1] = -b[i+1];
		} 
		else if(s[i] == '*'){
			b[i+1] *= b[i];
			b[i] = 0;
		}
	}
	int sum = 0;
	for(int i = 0; i < n; i++) sum += b[i];
	if(sum == 24) f = 1;
}

void dfs(string s){
	if(s.size() == n - 1){
		f = 0;
		com(s);
		if(f) ans++;
		return ;
	}
	for(int i = 0; i < dir.size(); i++){
		dfs(s + dir[i]);
	}
}

int main(){
	cin >> n;
	f = 0;
	for(int j = 0; j < n; j++){
		cin >> a[j];
	}
	dfs("");
	cout << ans;
	return 0;
}

1966. 人造星空

问题描述

A 市利用无人机制造了一个 n×m 大小的人造星空,在这个 n×m 大小的星空中,每个点都有一个无人机,无人机有发光和不发光两种不同的状态,对于所有的发光点,在空中就能形成独特的星空图形。

图形中有多个不同的图案,同一个图案的定义是这样的,对于两个发光的点,如果他们的曼哈顿距离(对于 A(x1,y1) 和 B(x2​,y2​) ,A 和 B 之间的曼哈顿距离为 ∣x1​−x2​∣+∣y1​−y2​∣ )小于等于 2 ,那么这两个点就属于一个图案。

请你编程计算一下,这个 n×m 的图形中,有多少个不同的图案。

比如:一个 6×6 的图形如下,该图形中有 2 个符合条件的图案。

-#----
##----
--##--
------
-#----
--#-##

输入

第一行,两个数 n 和 m 。(1 ≤n,m ≤ 100)

接下来一共 n 行,每行 m 个字符。对于第 ii 行第 j 个字符,如果其为 - ,那么表示该点不发光,如果其为 # ,那么表示该点发光。不可能出现其他的字符。

输出

输出一个整数,代表图案的个数。

样例

输入

6 6

-#----

##----

--##--

------

-#----

--#-##

输出

2

解析:本题是一道12联通的题目,在基础的8联通上,多加了上下左右前进两格,而对角线前进两格的长度大于2,所以是8+4 = 12联通。接下来就是枚举全部格子,找到为'#'就记录有一个图案,使用深度优先搜索把与之相邻的'#'变成'-'即可。

#include 
using namespace std;

int n, m, sum;
char ma[105][105];
int dir[12][2] = {{0, 1}, {-1, 1}, {1, 0}, {1, 1}, {0, -1}, {1, -1}, {-1, 0}, {-1, -1}, {0, 2}, {2, 0}, {0, -2}, {-2, 0}};

void dfs(int x, int y){
	for(int i = 0; i < 12; i++){
		int xx = x + dir[i][0], yy = y + dir[i][1];
		if(xx < 1 || yy < 1 || xx > n || yy > m || ma[xx][yy] == '-') continue;
		ma[xx][yy] = '-';
		dfs(xx, yy);
	}
}

int main() {
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			cin >> ma[i][j];
		}
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			if(ma[i][j] == '#'){
				ma[i][j] = '-';
				dfs(i, j);
				sum++;
			}
		}
	}
	cout << sum;
	return 0;
}

1914. 小H回家

问题描述

小H在一个划分成了n*m个方格的长方形封锁线上。 每次他能向上下左右四个方向移动一格(当然小H不可以静止不动), 但不能离开封锁线,否则就被打死了。 刚开始时他有满血6点,每移动一格他要消耗1点血量。一旦小H的 血量降到 0, 他将死去。 他可以沿路通过拾取金币来补满血量。只要他走到有金币的格子,他不需要任何时间即可拾取。格子上的金币可以瞬间补满,所以每次经过这个格子都有金币。就算到了某个有金币的格子才死去, 他也不能通过拾取金币补满血。 即使在家门口死去, 他也不能算完成任务回到家中。

地图上有 5 种格子:

数字 0: 障碍物。
数字 1: 空地, 小H可以自由行走。
数字 2: 小H出发点, 也是一片空地。
数字 3: 小H的家。
数字 4: 有金币在上面的空地。
小H能否安全回家?如果能, 最短需要多长时间呢?

输入

第一行两个整数n,m, 表示地图的大小为n*m。

下面 n 行, 每行 m 个数字来描述地图。

(1 <= n,m <= 9)

输出

一行, 若小H不能回家, 输出-1,否则输出他回家所需最短时间。

样例

输入

3 3

2 1 1

1 1 0

1 1 3

输出

4

解析:使用二维数组记录到达每一格子的最优方案(最小步数和最多血量),如果到达该格子的方案大于等于记录的方案,则认为不是最优,不需要继续搜索。

#include 
using namespace std;

int n, m, ans = 1e9, xq, yq, xz, yz;
int ma[15][15];
int dir[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
// max_hp 数组用于记录到达每个位置时的最大生命值
// min_step 数组用于记录到达每个位置时的最小步数
int max_hp[15][15], min_step[15][15];

// 深度优先搜索函数,用于搜索小 H 回家的路径
// x, y 表示当前位置的横纵坐标
// hp 表示当前的生命值
// step 表示当前已经走过的步数
void dfs(int x, int y, int hp, int step){
    // 如果生命值为 0,则无法继续前进,返回
    if(hp == 0)return ;
    // 最优性剪枝:如果当前到达该位置的生命值小于等于之前记录的最大生命值,
    // 且当前到达该位置的步数大于等于之前记录的最小步数,说明当前路径不是更优的,返回
    if(hp <= max_hp[x][y] && min_step[x][y] <= step) return ; 
    // 更新到达当前位置的最大生命值
    max_hp[x][y] = hp;
    // 更新到达当前位置的最小步数
    min_step[x][y] = step;
    // 如果当前位置是恢复生命值的点(值为 4),则将生命值恢复为 6
    if(ma[x][y] == 4) hp = 6;
    // 如果当前位置是家(终点),则更新回家的最少步数
    if(x == xz && y == yz){
        ans = min(step, ans);
    }
    // 遍历四个方向
    for(int i = 0; i < 4; i++){
        // 计算下一个位置的横纵坐标
        int xx = x + dir[i][0], yy = y + dir[i][1];
        // 如果下一个位置超出地图范围或者是障碍物(值为 0),则跳过
        if(xx < 1 || yy < 1 || xx > n || yy > m || ma[xx][yy] == 0) continue;
        // 递归调用 dfs 函数,继续搜索下一个位置,生命值减 1,步数加 1
        dfs(xx, yy, hp - 1, step + 1);
    }
}

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> ma[i][j];
            if(ma[i][j] == 2) xq = i, yq = j;
            else if(ma[i][j] == 3) xz = i, yz = j;
        }
    }
    // 从起始位置开始进行深度优先搜索,初始生命值为 6,初始步数为 0
    dfs(xq, yq, 6, 0);
    // 如果最终 ans 仍然是初始的较大值 1e9,说明无法回家,输出 -1
    if(ans == 1e9) cout << -1;
    // 否则输出回家的最少步数
    else cout << ans;
    return 0;
}

你可能感兴趣的:(深度优先搜索DFS深搜综合2(1832. 八皇后问题、1833. 八皇后、1955. 算24点、1956. 算24点(2)、1966. 人造星空、1914. 小H回家))