题解:B3791 [信息与未来 2023] 电路布线

题解:B3791 [信息与未来 2023] 电路布线

前言

题目传送门

没想到有这么水的蓝题,还可以写题解。

看到有大佬写了 187 行超长代码,根本不用那么复杂,我就补一篇短一点的题解把。

思路讲解

很明显可以搜索。

暴搜 dfs 枚举每一个可以接通的点,判断行不行,行的话考虑接通或不接通。不行只能不接通。

时间复杂度为 \(2^{n\times m}\times nm\),明显 TLE。

必须剪枝。

大多数 dfs 的剪枝都是预估能不能超过答案,不能就返回,这个题目也不例外。

对于一层搜索,判断其搜索完能不能超过之前的答案,不能就退出。

判断方法也是十分简单,看一下目前已连接的点加上未访问过的所有点是否 \(\le ans\),是的话就退出。

剪枝讲完了讲 check 函数,这个函数需要判断两件事情:

  • 是否联通
  • 是否短路

是否联通很好判断,问题是怎么判断是否短路。

只需要一直走走走,如果发现走着走着走到了之前标记过的点,并且这个点不是上一个访问的点,就短路了。

AC Code

注释版:

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

// 定义常量
const int MAXN = 17; // 网格最大尺寸
const int dx[] = {0, 1, 0, -1}; // 方向数组:右、下、左、上
const int dy[] = {1, 0, -1, 0}; // 方向数组:右、下、左、上

// 全局变量
int n, m; // 网格的行数和列数
int cur; // 初始必须布线的 '+' 数量
int cnt; // 用于检查连通性时计数
char mp[MAXN][MAXN]; // 存储原始网格
char answer[MAXN][MAXN]; // 存储最优解
int sum_x, sum_y; // 初始 '+' 的坐标(用于连通性检查的起点)
bool vis[MAXN][MAXN]; // 访问标记数组
int ans = 0; // 最优解中 '+' 的数量

// 计算从当前格子到网格末尾的剩余格子数(用于剪枝)
inline int f(int x, int y) {
    return n * m - (x - 1) * m - y;
}

// 检查连通性和无回路
// (x, y): 当前格子
// (x_x, y_y): 父格子(避免重复访问)
inline bool check(int x, int y, int x_x, int y_y) {
    if (vis[x][y]) {
        return false; // 如果已经访问过,说明存在回路
    }
    vis[x][y] = true; // 标记为已访问
    cnt--; // 剩余未访问的 '+' 数量减一
    for (int i = 0; i < 4; i++) {
        int nx = x + dx[i], ny = y + dy[i]; // 四个方向移动
        if (x_x == nx && y_y == ny) {
            continue; // 跳过父格子
        }
        // 检查邻居是否是 '+' 且未访问
        if (1 <= nx && nx <= n && 1 <= ny && ny <= m && mp[nx][ny] == '+') {
            if (!check(nx, ny, x, y)) {
                return false; // 如果邻居检查失败,直接返回 false
            }
        }
    }
    return true; // 所有邻居检查通过
}

// 深度优先搜索
// (x, y): 当前格子坐标
// sum: 当前已布线的 '+' 数量
inline void dfs(int x, int y, int sum) {
    // 换行处理
    if (y > m) {
        x++;
        y = 1;
    }
    // 剪枝:如果当前 sum 加上剩余格子数不超过已知最优解,直接返回
    if (f(x, y) + sum <= ans) {
        return;
    }
    // 终止条件:遍历完所有格子
    if (x > n) {
        memset(vis, false, sizeof(vis)); // 重置访问标记
        cnt = sum; // 设置需要检查的 '+' 数量
        // 检查连通性和无回路
        if (check(sum_x, sum_y, -1, -1) && !cnt) {
            memcpy(answer, mp, sizeof(mp)); // 保存当前最优解
            ans = sum; // 更新最优解数量
        }
        return;
    }
    // 如果当前格子是 '.',尝试布线
    if (mp[x][y] == '.') {
        mp[x][y] = '+'; // 布线
        memset(vis, false, sizeof(vis)); // 重置访问标记
        cnt = sum + 1; // 设置需要检查的 '+' 数量
        // 检查连通性和无回路
        if (check(x, y, -1, -1)) {
            dfs(x, y + 1, sum + 1); // 递归搜索下一个格子
        }
        mp[x][y] = '.'; // 回溯,取消布线
    }
    // 不布线的情况
    dfs(x, y + 1, sum);
}

int main() {
    // 输入网格
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mp[i][j];
            if (mp[i][j] == '+') {
                cur++; // 统计初始 '+' 数量
                sum_x = i; // 记录初始 '+' 的坐标
                sum_y = j;
            }
            answer[i][j] = mp[i][j]; // 初始化最优解
        }
    }
    // 开始深度优先搜索
    dfs(1, 1, cur);
    // 输出最优解
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cout << answer[i][j];
        }
        cout << endl;
    }
    return 0;
}

无注释版:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 17;
const int dx[] = {0, 1, 0, -1};
const int dy[] = {1, 0, -1, 0};
int n, m;
int cur, cnt;
char mp[MAXN][MAXN];
char answer[MAXN][MAXN];
int sum_x, sum_y;
bool vis[MAXN][MAXN];
int ans = 0;
inline int f(int x, int y)
{
    return n * m - (x - 1) * m - y;
}
inline bool check(int x, int y, int x_x, int y_y)
{
    if (vis[x][y])
    {
        return false;
    }
    vis[x][y] = true;
    cnt--;
    for (int i = 0; i < 4; i++)
    {
        int nx = x + dx[i], ny = y + dy[i];
        if (x_x == nx && y_y == ny)
        {
            continue;
        }
        if (1 <= nx && nx <= n && 1 <= ny && ny <= m && mp[nx][ny] == '+')
        {
            if (!check(nx, ny, x, y))
            {
                return false;
            }
        }
    }
    return true;
}
inline void dfs(int x, int y, int sum)
{
    if (y > m)
    {
        x++;
        y = 1;
    }
    if (f(x, y) + sum <= ans)
    {
        return;
    }
    if (x > n)
    {
        memset(vis, false, sizeof(vis));
        cnt = sum;
        if (check(sum_x, sum_y, -1, -1) && !cnt)
        {
            memcpy(answer, mp, sizeof(mp));
            ans = sum;
        }
        return;
    }
    if (mp[x][y] == '.')
    {
        mp[x][y] = '+';
        memset(vis, false, sizeof(vis));
        cnt = sum + 1;
        if (check(x, y, -1, -1))
        {
            dfs(x, y + 1, sum + 1);
        }
        mp[x][y] = '.';
    }
    dfs(x, y + 1, sum);
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cin >> mp[i][j];
            if (mp[i][j] == '+')
            {
                cur++;
                sum_x = i;
                sum_y = j;
            }
            answer[i][j] = mp[i][j];
        }
    }
    dfs(1, 1, cur);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cout << answer[i][j];
        }
        cout << endl;
    }
    return 0;
}
posted @ 2026-01-02 17:08  fengjunxiao2014  阅读(0)  评论(0)    收藏  举报