📜  门|门 CS 1996 |第 44 题(1)

📅  最后修改于: 2023-12-03 15:28:47.508000             🧑  作者: Mango

门|门 CS 1996 |第 44 题

这是一道经典的计算机竞赛题目,涉及到图论和搜索算法。下面是一份详细的解析和实现示例。

题意

有 $n$ 个大门和 $m$ 条双向边,构成了一个有向图。对于每个大门,都有一个状态 $s_i$ 表示它是否已经打开($s_i=0$ 表示未打开,$s_i=1$ 表示已打开)。现在,你需要找到一些门,使得按照任意顺序经过这些门后,这些门状态都变为已打开。请输出最小门数和满足条件的一种方案。

解析

这是一个典型的图问题,可以使用广度优先搜索或深度优先搜索进行求解。我们可以将每个门看作图中的一个节点,门之间的通行关系看作节点之间的边。然后,我们从初始节点(所有门状态都未打开)开始搜索,通过一系列边的遍历,尝试找到一种方案,使得所有节点都被遍历到,并且满足题目所给的条件。

为了求出最小门数和方案,我们需要在搜索过程中保存一些信息。具体来说,我们可以开一个结构体,保存以下信息:

  • 该状态下已经经过的门的编号。
  • 该状态下各个门的当前状态(即打开或未打开)。
  • 该状态下还需要经过哪些门才能满足要求。
  • 该状态下经过门的个数。

当我们搜索到一个新状态时,需要依次判断该状态是否满足以下条件:

  • 该状态下还需要经过门的个数是否为 0(即所有门都已被经过)。
  • 该状态下经过门的个数是否小于当前最小门数。
  • 该状态下是否存在一个可用门(即未打开)可供经过。

如果满足以上条件,则更新最小门数和方案,并停止搜索。否则,将当前状态的所有合法后继状态加入队列中,继续搜索。注意,为了避免重复经过同一个门,我们在更新后继状态时需要将当前门的状态标记为已开启,以避免重复遍历。

代码

以下是一个参考实现,使用了广度优先搜索。其中 node 结构体表示状态节点,search 函数实现了搜索过程。

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

struct node {
    vector<int> doors;  // 已经经过的门编号
    vector<int> status; // 各个门的状态(0:未打开,1:已打开)
    vector<int> remain; // 需要经过的门编号
    int cnt;            // 经过的门数量
};

int bfs(int n, int m, vector<vector<int>>& graph) {
    node start;
    start.doors = {};
    start.status = vector<int>(n, 0);
    start.remain = vector<int>(n, 1);
    start.cnt = 0;
    queue<node> q;
    q.push(start);
    int min_doors = INT_MAX;
    vector<int> res_doors;
    while (!q.empty()) {
        node cur = q.front();
        q.pop();
        if (cur.remain.empty() && cur.cnt < min_doors) {
            min_doors = cur.cnt;
            res_doors = cur.doors;
            continue;
        }
        if (cur.cnt + 1 >= min_doors) {
            continue;
        }
        for (int i = 0; i < n; i++) {
            if (cur.status[i] == 1 || cur.remain[i] == 0) {
                continue;
            }
            node next = cur;
            next.status[i] = 1;
            next.remain[i] = 0;
            next.doors.push_back(i + 1);
            next.cnt++;
            for (int adj : graph[i]) {
                next.remain[adj] = 0;
            }
            q.push(next);
        }
    }
    cout << "Minimum number of doors: " << min_doors << endl;
    cout << "Door sequence: ";
    for (int door : res_doors) {
        cout << door << " ";
    }
    cout << endl;
    return min_doors;
}

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> graph(n);
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        graph[u - 1].push_back(v - 1);
        graph[v - 1].push_back(u - 1);
    }
    int result = bfs(n, m, graph);
    return 0;
}

以上代码可以输出最小门数和一种满足条件的方案。如果需要输出所有方案,则需要对 node 结构体进行修改,保存所有合法路径信息,不在本文讨论范围之内。

总结

本题虽然并没有什么特别高深的算法,但是对于初学者来说仍然是一份不错的练习题目。通过本题的练习,可以获得以下收获:

  • 掌握广度优先搜索、深度优先搜索等基础算法。
  • 掌握结构体的使用方法,尤其是在搜索过程中保存一些信息。
  • 锻炼代码实现能力,特别是图论相关算法的实现能力。