八皇后问题&最小换乘问题

这道问题很精辟, 考察回溯的实现。

对于一个二维数组何如回溯的思想很有帮助。

由于我找到一个写得非常不错的文章, 我就不在这里自取欺辱了。

大佬的八皇后思想分解

基于这道题的思想, 我自己对于二维数组的回溯实现有了一定的了解。
还是很有帮助的, 本章文件就相当于是一篇转发吧。

核心框架:

void Find_Queen(vector<vector<int>>& queen_pos, int rows, int* steps) {
	//rows 控制 行数, 当rows == 8时,代表一个成功的结果
	if (rows == 8) {
		(*steps)++;
		//Print(queen_pos);	//打印八皇后的位置
		return;
	}

	for (int cols = 0; cols < 8; cols++) {
		if (Check(queen_pos, rows, cols)) {
			//该位置为合格为止
			queen_pos[rows][cols] = 1;					//一个皇后存在该位置
			Find_Queen(queen_pos, rows + 1, steps);		//判断下一层皇后的位置
			queen_pos[rows][cols] = 0;					//回溯回来 - 该层位置不合适 - 换位置
		}
	}
}

传入的参数rows控制层数,函数里面的for循环中cols控制每一层的列数。
如果这一层有合适的插入位置, 就换一行处理

Find_Queen(queen_pos, rows + 1, steps);

如果这一层没有合适的位置,这里时候这个函数栈就会退出,返回到上一层调用栈, 由于rows没有到8, 所以回溯到上一层, 将这一层的位置清除掉,因为这个位置不合理。

queen_pos[rows][cols] = 0;

其实根据这个框架, 我们设计出二维数组的回溯问题, 随机组合问题等。
框架还是很重要的。

后面是我写得代码:

#include <iostream>
#include <vector>

using namespace std;

bool Check(const vector<vector<int>>& queen_pos, int row, int col) {
	
	//行列判断
	for (int i = 0; i < 8; i++) {
		if (queen_pos[row][i] == 1 || queen_pos[i][col] == 1) {		
			return false;
		}
	}

	//左上角判断
	for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
		if (queen_pos[i][j] == 1) { return false; }
	}
	//右下角
	for (int i = row + 1, j = col + 1; i < 8 && j < 8; i++, j++) {
		if (queen_pos[i][j] == 1) { return false; }
	}
	//左下角
	for (int i = row + 1, j = col - 1; i < 8 && j >= 0; i++, j--) {
		if (queen_pos[i][j] == 1) { return false; }
	}
	//右上角
	for (int i = row - 1, j = col + 1; i >= 0 && j < 8; i--, j++) {
		if (queen_pos[i][j] == 1) { return false; }
	}

	return true;
}

void Print(const vector<vector<int>>& vec) {
	for (const auto &vit : vec) {
		for (const auto& vvit : vit) {
			cout << vvit << ' ';
		}
		cout << endl;
	}
	cout << endl;
}

void Find_Queen(vector<vector<int>>& queen_pos, int rows, int* steps) {
	//rows 控制 行数, 当rows == 8时,代表一个成功的结果
	if (rows == 8) {
		(*steps)++;
		//Print(queen_pos);	//打印八皇后的位置
		return;
	}

	for (int cols = 0; cols < 8; cols++) {
		if (Check(queen_pos, rows, cols)) {
			//该位置为合格为止
			queen_pos[rows][cols] = 1;					//一个皇后存在该位置
			Find_Queen(queen_pos, rows + 1, steps);		//判断下一层皇后的位置
			queen_pos[rows][cols] = 0;					//回溯回来 - 该层位置不合适 - 换位置
		}
	}
}

int main() {

	vector<vector<int>> queen_pos(8);
	for (auto& vit : queen_pos) {
		vit.resize(8, 0);
	}

	//0 -- 代表不存在, 1 代表存在

	int rows = 0;
	int steps = 0;
	Find_Queen(queen_pos, rows, &steps);

	cout << "八皇后共有:" << steps << endl;
	return 0;
}

测试结果:
在这里插入图片描述
二、换乘问题

小明从起始站到终点站, 一共有4站, 每一站有三种选择, 分别为公交、火车、地铁。
1、每一站的公交、火车、地铁的价钱不一样。
2、你只能换乘2次, (上一站公交,下一站坐火车或地铁就是换乘一次)

公交、火车、地铁
第一站:1 6 7
第二站:4 2 3
第三站:5 3 4
第四站:4 7 7

在这里插入图片描述
实现代码:

#include <iostream>
#include <vector>

using namespace std;

int min_path = 0;

void FindMinPath(const vector<vector<int>>& vec, int rows, int flag, int &count, vector<int> &path) {
	if (rows == 4) {
		int sum = 0;
		for (const auto& vit : path) {
			sum += vit;
		}

		if (!min_path) { min_path = sum; }
		else if (min_path > sum) { min_path = sum; }

		return;
	}

	for (int cols = 0; cols < 3; cols++) {
		
		if (rows != 0) {
			//不是第一层, 存在换乘的问题
			if (flag != cols) {
				//换乘
				count--;							//换乘计数器--
				if (count < 0) {
					count++;						//换乘次数已经用完, 因此直接空过, count++, 很容易忽略的问题 
					continue;						//这一趟cols车不匹配, 换一乘判断
				}
			}
		}

		path.push_back(vec[rows][cols]);		//插入
		FindMinPath(vec, rows + 1, flag, count, path);
		path.pop_back();						//回溯
		if (flag != cols) {
			//之前进来, 换乘计数器--, 因此回溯的时候, count++
			count++;
		}
	}

}

int main() {

	vector<vector<int>> vec(4);
	for (auto& it : vec) {
		it.resize(3);
	}

	int arr[4][3] = {
		{1, 6, 7},
		{4, 2, 3},
		{5, 3, 4},
		{4, 7 ,7}
	};

	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 3; j++) {
			vec[i][j] = arr[i][j];
		}
	}

	int rows = 0;			//层数
	int flag = 0;			//标记上一层是什么车, 用于匹配是否换乘问题
	int count = 2;			//换成计数器

	vector<int> path;		//存储换乘的价值

	FindMinPath(vec, rows, flag, count, path);

	cout << "最小换乘2次时, 到达目的地最小价值为:" << min_path << endl;

	return 0;
}

测试结果:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值