C++分支限界法实战:封闭区域填充、电梯调度与最短路径算法详解

Alice
Alice
发布时间:2026-10-16| 5人浏览过
原创

分支限界法是求解题空间中最优解的重要方法,常借助广度优先搜索(BFS)按层扩展节点。本文将通过 C++ 实现三个典型案例:一是从矩阵边缘向外扩散以标记并填充内部封闭区域;二是模拟电梯按键策略以最小化操作次数;三是利用分层 BFS 计算网格地图中的最短通行路径。

基础应用:封闭区域识别与填充

第1步:识别并填充单组封闭区域

目的:解决在一个由 0 和 1 组成的 n x n 方阵中,找出被 1 完全包围的内部 0,并将其替换为 2 的问题。

入口与思路:直接定位内部封闭区域较难,采用逆向思维——所有不封闭的区域必然与矩阵的最外围边界相连。因此,从矩阵的边缘出发进行 BFS 搜索,所有能访问到的 0 都是非封闭区域,将其标记后,剩余未被访问的 0 即为需要填充的目标区域。

C++ 操作代码:

#include<bits/stdc++.h>
using namespace std;
const int M = 31;
int Map[M][M]; // 记录输入格子的情况
bool vis[M][M] = { false }; // 标记格子访问情况,默认未访问
int n;
queue <int > q;

void bfs(int x, int y) { // 从边界点出发的广度优先搜索
    int dir[4][2] = { {-1,0},{1,0},{0,-1},{0,1} }; 
    vis[x][y] = true; 
    q.push(x);q.push(y);
    while (!q.empty()) {
        int w = q.front();
        q.pop();
        int e = q.front();
        q.pop();
        for (int i = 0;i < 4;i++) {
            int now_x = w + dir[i][0];
            int now_y = e + dir[i][1];
            // 检查是否越界、是否为边界(1)或已访问点
            if (1 <= now_x && now_x <= n && 1 <= now_y && now_y <= n && Map[now_x][now_y] == 0 && !vis[now_x][now_y]) {
                vis[now_x][now_y] = true;
                q.push(now_x);q.push(now_y);
            }
        }
    }
}
int main()
{
    cin >> n;
    for (int i = 1;i <= n;i++) {
        for (int j = 1;j <= n;j++) {
            cin >> Map[i][j];
            if (Map[i][j] == 1)
                vis[i][j] = true; // 边界如果是1,则视为不可通达
        }
    }
    // 从第一行开始横向扫描
    for (int j = 1; j <= n;j++) {
        if (!vis[1][j]) bfs(1, j);
    }
    // 从最后一行开始横向扫描
    for (int j = 1; j <= n;j++) {
        if (!vis[n][j]) bfs(n, j);
    }
    // 从最左边一列纵向扫描
    for (int i = 2;i < n;i++) {
        if (!vis[i][1]) bfs(i, 1);
    }
    // 从最右边一列纵向扫描
    for (int i = 2;i < n;i++) {
        if (!vis[i][n]) bfs(i, n);
    }

    for (int i = 1;i <= n;i++) {
        for (int j = 1;j <= n;j++) {
            if (vis[i][j]) // 属于非封闭区域
                cout << Map[i][j] << " ";
            else
                cout << 2 << " "; // 封闭区域内的0输出为2
        }
        cout << endl;
    }
    return 0;
}

第2步:寻找最大封闭区域并填充

目的:当存在多个封闭区域时,仅将面积最大的那个封闭区域内的 0 填充为 2。

入口与思路:首先需要彻底排除外部连通的影响。通过在数组外围虚构一圈 0 并对此区域执行 DFS 染色,可以确保任何与外部连通的 0 都被标记。随后在有效区域内对所有未访问的 0 进行遍历计数,保留面积最大的一组坐标标记。

C++ 操作代码:

#include<bits/stdc++.h>
using namespace std;
int a[35][35] = { 0 };
int n;
int dir[4][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
int cnt = 0;
int cnt_max = 0;
int id = 3; // 用于染色的标记值
int id_max = id;

void prit() {
	int i, j;
	for (i = 1; i <= n; i++) {
		for (j = 1; j <= n; j++) {
			if (a[i][j] == 1) {
				cout << a[i][j] << ' ';
			}
			else if (a[i][j] != id_max) {
				cout << '0' << ' ';
			}
			else {
				cout << '2' << ' '; // 仅最大封闭区输出2
			}
		}
		cout << endl;
	}
}

void dfs(int x, int y){
	// 越界或目标点不是0则返回
	if (a[x][y] != 0 || x < 0 || x > n + 1 || y < 0 || y > n + 1) { 
		return;
	}
	a[x][y] = id;
	cnt++;
	for (int i = 0; i < 4; i++) { 
		dfs(x + dir[i][0], y + dir[i][1]);
	}
}
int main() {
	int i, j;
	cin >> n;
	for (i = 1; i <= n; i++) 
		for (j = 1; j <= n; j++) 
			cin >> a[i][j];
	
	// 1. 先给最外圈多出的0进行初始染色,消除外部影响
	dfs(0, 0);
	id++;
	
	// 2. 在有效范围内(从第2层到倒数第2层)寻找封闭区域
	for (i = 2; i < n; i++) {
		for (j = 2; j < n; j++) {
			cnt = 0;
			dfs(i, j);
			if (cnt > cnt_max) { 
				cnt_max = cnt;
				id_max = id;
			}
			id++; 
		}
	}
	prit();
	return 0;
}

进阶应用:状态空间搜索与最短路径

第3步:解决电梯按钮最短调度问题

目的:在每层楼有固定升降规则(上 Ki 层或下 Ki 层)的电梯系统中,计算从 A 层到达 B 层的最少按键次数。

入口与思路:这是一个典型的无权图最短路径问题。每一层楼作为图的节点,可达的下一层楼作为边。使用 BFS 进行搜索,首次到达 B 层时的搜索深度即为最少按键数。

C++ 操作代码:

#include<bits/stdc++.h>
using namespace std;

int n, a, b, k[210];
bool f[210] = { false }; // 标记楼层是否访问过

void bfs() {
    queue<pair<int, int> > q; 
    pair<int, int> p, t; 
    p.first = a; p.second = 0; // 当前楼层,已按键次数
    q.push(p);
    while (!q.empty()) {
        p = q.front(); q.pop();
        if (f[p.first]) 
            continue;
        f[p.first] = true;
        if (p.first == b) { // 找到目标层
            cout << p.second << endl;
            return;
        }
        // 尝试向下按
        if (p.first - k[p.first] > 0) {
            t.first = p.first - k[p.first];
            t.second = p.second + 1;
            q.push(t);
        }
        // 尝试向上按
        if (p.first + k[p.first] <= n) {
            t.first = p.first + k[p.first];
            t.second = p.second + 1;
            q.push(t);
        }
    }
    cout << -1 << endl; // 无法到达
}

int main() {
    cin >> n >> a >> b;
    for (int i = 1; i <= n; i++)
        cin >> k[i];
    bfs(); 
    return 0;
}

第4步:求解网格图中的最短通行距离

目的:在一个包含障碍(-1)和空地(0)的二维地图上,求出从起点到终点的最短步数,并输出带有距离标识的网格状态。

入口与思路:采用标准的层次 BFS。关键在于利用队列特性区分不同“轮次”。在循环开始前保存当前层的节点数量 s,逐层向外扩展。每当结束一轮处理,计数器 num 加 1。这样既记录了每个点的步数,也便于最后统一输出符合特定格式的结果。

C++ 操作代码:

#include<bits/stdc++.h>
using namespace std;
int Map[21][21] = { 0 }; // 记录区域信息
int m, n;
queue <int > q; 
int num = 1; // 记录当前是第几轮搜索(即步数)

void bfs(int sx, int sy, int ex, int ey) {
	int dir[4][2] = { {-1,0},{1,0},{0,-1},{0,1} }; 
	q.push(sx); q.push(sy);
	while (!q.empty()) {
		int s = q.size() / 2; // 当前这一层有多少个节点
		for (int k = 0;k < s;k++) {
			int cur_x = q.front();
			q.pop();
			int cur_y = q.front();
			q.pop();
			for (int i = 0;i < 4;i++) {
				int now_x = cur_x + dir[i][0];
				int now_y = cur_y + dir[i][1];
				if (now_x == ex && now_y == ey)  // 命中终点
					return;
				// 判断边界、是否可通行
				if (0 <= now_x && now_x < n && 0 <= now_y && now_y < m && Map[now_x][now_y] == 0) { 
					q.push(now_x);q.push(now_y);
					Map[now_x][now_y] = num; // 标记该点在第几轮被访问
				}
			}
		}
		num++; // 完成一层,层数+1
	}
}
int main() {
	int sx, sy, ex, ey;
	cin >> m >> n;
	cin >> sx >> sy >> ex >> ey;
	for (int i = 0;i < m;i++)
		for (int j = 0;j < n;j++)
			cin >> Map[i][j];
	bfs(sx, sy, ex, ey);
	
	for (int i = 0;i < m;i++) {
		for (int j = 0;j < n;j++) {
			if (i == sx && j == sy) 
				cout << "  a";
			else if (i == ex && j == ey) 
				cout << "  b";
			else if (Map[i][j] == num) // 与终点同层到达的点
				cout << "  0";
			else if (Map[i][j] < num) // 走过的路,显示步数
				printf("%3d", Map[i][j]);
		}
		cout << endl;
	}
	return 0;
}

总结

通过上述四个实验,我们可以看出分支限界法在 C++ 中的实现高度依赖队列结构和边界控制。无论是矩阵边界向外探测、状态树逐层扩展,还是网格坐标位移,掌握正确的入队出队逻辑与终止条件是实现高效算法的关键。以上就是C++分支限界法在算法题目中具体应用的详细内容,更多关于C++ 算法设计的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。