题解

25 条题解

  • 1
    @ 2026-03-11 12:02:55

    有点坑,ai改了几个版本再结合起来才AC。
    代码仅供参考。

    #include <iostream>
    #include <vector>
    #include <stack>
    #include <set>
    #include <queue>
    #include <algorithm>
    using namespace std;
    
    // 上下左右四个方向
    const int dx[] = {-1, 1, 0, 0};
    const int dy[] = {0, 0, -1, 1};
    
    int n, m;
    vector<vector<int>> grid;
    int bx = -1, by = -1; // 电池坐标
    
    // ---------------------- 电源短路检测 ----------------------
    // BFS检查两点在无灯泡的子图中是否连通(电池位置视为不可通过)
    bool bfs_check_connect(int sx, int sy, int tx, int ty) {
        vector<vector<bool>> visited(m, vector<bool>(n, false));
        queue<pair<int, int>> q;
        q.push({sx, sy});
        visited[sx][sy] = true;
    
        while (!q.empty()) {
            auto [x, y] = q.front();
            q.pop();
            if (x == tx && y == ty) return true;
            for (int d = 0; d < 4; ++d) {
                int nx = x + dx[d];
                int ny = y + dy[d];
                // 边界检查,不能是电池位置,不能是0/灯泡,未访问
                if (nx < 0 || nx >= m || ny < 0 || ny >= n) continue;
                if (nx == bx && ny == by) continue; // 移除电池
                if (grid[nx][ny] == 0 || grid[nx][ny] == 3) continue; // 障碍物/灯泡都不可通过
                if (!visited[nx][ny]) {
                    visited[nx][ny] = true;
                    q.push({nx, ny});
                }
            }
        }
        return false;
    }
    
    // 检测是否存在电源短路(电池在无灯泡的环里)
    bool has_power_short_circuit() {
        vector<pair<int, int>> neighbors;
        // 收集电池在无灯泡子图中的邻居
        for (int d = 0; d < 4; ++d) {
            int nx = bx + dx[d];
            int ny = by + dy[d];
            if (nx >= 0 && nx < m && ny >= 0 && ny < n && grid[nx][ny] == 1) {
                neighbors.emplace_back(nx, ny);
            }
        }
        // 邻居数小于2,不可能形成环
        if (neighbors.size() < 2) return false;
        // 检查任意两个邻居是否连通,连通则说明电池在环里
        for (int i = 0; i < neighbors.size(); ++i) {
            for (int j = i + 1; j < neighbors.size(); ++j) {
                if (bfs_check_connect(neighbors[i].first, neighbors[i].second, neighbors[j].first, neighbors[j].second)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    // ---------------------- 点双连通分量求解(统计能亮的灯泡) ----------------------
    vector<vector<int>> dfn, low;
    int time_stamp = 0;
    stack<pair<pair<int, int>, pair<int, int>>> edge_stack;
    vector<set<pair<int, int>>> bcc_list;
    
    void tarjan(int x, int y, int parent_x, int parent_y) {
        dfn[x][y] = low[x][y] = ++time_stamp;
        int child_cnt = 0;
    
        for (int d = 0; d < 4; ++d) {
            int nx = x + dx[d];
            int ny = y + dy[d];
            // 越界、障碍物、父节点,跳过
            if (nx < 0 || nx >= m || ny < 0 || ny >= n) continue;
            if (grid[nx][ny] == 0) continue;
            if (nx == parent_x && ny == parent_y) continue;
    
            if (dfn[nx][ny] == 0) {
                child_cnt++;
                edge_stack.push({{x, y}, {nx, ny}});
                tarjan(nx, ny, x, y);
                low[x][y] = min(low[x][y], low[nx][ny]);
    
                // 发现割点,提取点双连通分量
                if ((parent_x == -1 && parent_y == -1 && child_cnt > 1) 
                    || (parent_x != -1 && low[nx][ny] >= dfn[x][y])) {
                    set<pair<int, int>> bcc;
                    while (true) {
                        auto edge = edge_stack.top();
                        edge_stack.pop();
                        bcc.insert(edge.first);
                        bcc.insert(edge.second);
                        if (edge.first == make_pair(x, y) && edge.second == make_pair(nx, ny)) {
                            break;
                        }
                    }
                    bcc_list.push_back(bcc);
                }
            } else if (dfn[nx][ny] < dfn[x][y]) { // 回边
                edge_stack.push({{x, y}, {nx, ny}});
                low[x][y] = min(low[x][y], dfn[nx][ny]);
            }
        }
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
    
        cin >> n >> m;
        grid.resize(m, vector<int>(n));
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                cin >> grid[i][j];
                if (grid[i][j] == 2) {
                    bx = i;
                    by = j;
                }
            }
        }
    
        // 步骤1:检测电源短路
        if (has_power_short_circuit()) {
            cout << "Error" << endl;
            return 0;
        }
    
        // 步骤2:求解原始图的点双连通分量
        dfn.resize(m, vector<int>(n, 0));
        low.resize(m, vector<int>(n, 0));
        tarjan(bx, by, -1, -1);
        // 处理栈中剩余的边(根节点所在的点双)
        if (!edge_stack.empty()) {
            set<pair<int, int>> bcc;
            while (!edge_stack.empty()) {
                auto edge = edge_stack.top();
                edge_stack.pop();
                bcc.insert(edge.first);
                bcc.insert(edge.second);
            }
            bcc_list.push_back(bcc);
        }
    
        // 步骤3:统计包含电池的点双中的灯泡,去重
        set<pair<int, int>> valid_bulbs;
        for (const auto& bcc : bcc_list) {
            if (bcc.count({bx, by})) {
                for (const auto& p : bcc) {
                    int x = p.first, y = p.second;
                    if (grid[x][y] == 3) {
                        valid_bulbs.insert(p);
                    }
                }
            }
        }
    
        // 步骤4:输出结果
        if (valid_bulbs.empty()) {
            cout << "Error" << endl;
        } else {
            cout << valid_bulbs.size() << endl;
        }
    
        return 0;
    }
    
  • 1
    @ 2008-08-02 13:47:03

    这是我出的题...

    怎么你们的程序都冗长...

  • 0
    @ 2015-06-03 22:44:41

    5 5
    1 1 1 3 1
    1 0 1 0 1
    1 3 1 0 1
    1 0 1 0 1
    1 1 1 2 1
    为什么是2?短路判断完直接数接线?

  • 0
    @ 2013-08-21 22:15:43

    行列坑爹啊,大家千万小心,别被题目猥琐欲为了!!

  • 0
    @ 2012-11-02 23:07:24

    咳咳,补充个数据范围:

    3

  • 0
    @ 2009-08-07 11:58:55

    我就一个DFS,弱弱的数据,沙茶法则都可以秒杀,哎~~~~~~

  • 0
    @ 2009-08-01 16:14:06

    第30个ac,简单的dfs也可以秒杀

  • 0
    @ 2009-07-28 22:27:15

    ........汗...

    编译通过...

    ├ 测试数据 01:答案正确... 0ms

    ├ 测试数据 02:答案正确... 0ms

    ├ 测试数据 03:答案正确... 0ms

    ├ 测试数据 04:答案正确... 0ms

    ├ 测试数据 05:答案正确... 0ms

    ---|---|---|---|---|---|---|---|-

    Accepted 有效得分:100 有效耗时:0ms

    .....难得....第一次难度3的进前30....还一次ac秒杀......

    先用checkshort判断是否短路..再用checkopen判断是否开路...

    最后用checknum找几个电灯泡在有电源的电路中...

    三个函数大同小异..主要用foodfill...

  • 0
    @ 2009-07-26 17:01:11

    这题目。。无语,我错误程序过了

  • 0
    @ 2009-04-13 22:23:47

    支持voyagec2的方法

    1.一直删除所有的非零数,如果它的周围四个中有不少于3个0,直到不能删

    统计剩下的灯泡

    2.删掉那些灯泡,然后重复"一直删除所有的非零数,如果它的周围四个中有不少于3个0,直到不能删"

    检测电池旁边是否有导线,有的话判定短路

  • 0
    @ 2009-02-03 21:45:43

    题质量不高.N,M=3 then begin flag:=false;map:=0; end;

    end;

    until flag;

    最后亮的灯泡就是修改了的电路中仍存在的灯泡

    这么做一次AC

  • 0
    @ 2008-10-20 15:01:06

    哦,原来灯泡短路和电路短路不同的概念!!!

    先一遍dfs判断是否短路。

    然后大家要千万注意行列的读法!!!

  • 0
    @ 2008-10-14 19:53:07
        ╭┓┏━━┓┏━┣━━┓╭┓ ┏┓ ┏━━┻━┓  
      ┣┳┏━━┓┃ ╯  ┃┏┣━┣━┓ ┏━━┓   
      ┃┃┃  ┃┗┳┗━┏╯╭┛ ┃ ┃ ┗━━┛   
      ┏┣┗━━╯┏┃━━╯┛┃  ┃ ┃┏━━━━┓  
       ┣╰┓╭┛┃┣┏━━┓┃  ┛━┛ ┏━━┓   
    囧 ,┗╯┗━━┛┗┻┗━━╯┗━━━━╯╰╯  ╰┛
    
  • 0
    @ 2008-10-07 20:40:28

    简单的深搜加FLOODFILL而已

    怎么就这么点人AC,无语了,一次AC庆贺下~~~~

  • 0
    @ 2008-10-07 11:39:50

    灯泡短路也算亮

    汗,,他的灯泡什么灯泡来的。。

  • 0
    @ 2008-10-07 09:28:20

    这个题实在是让人升不起做的欲望....

    灯泡短路也算亮.....

    什么理论啊

  • 0
    @ 2008-08-19 16:32:45

    把行跟列搞反了,WA了无数次,前四个点居然长宽相等,检查了半天才发现.

    5555555~~

  • 0
    @ 2008-07-22 22:09:31

    貌似样例2左边那个灯泡和导线并联啊,应该不会亮吧

  • 0
    @ 2008-07-25 20:36:18

    最后一个点...为什么老错...

  • 0
    @ 2008-07-19 21:59:44

    汗。。。灯泡短路也算亮,让我想挺久的。。

信息

ID
1373
难度
7
分类
搜索 | 搜索与剪枝 点击显示
标签
(无)
递交数
166
已通过
32
通过率
19%
被复制
2
上传者