所有可能的路径---对何时使用bfs+状态压缩的稍微总结

题目

dfs回溯

没啥可说的,究极简单+标准

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> stk;

    void dfs(vector<vector<int>>& graph, int x, int n) {
        if (x == n) {
            ans.push_back(stk);
            return;
        }
        for (auto& y : graph[x]) {
            stk.push_back(y);
            dfs(graph, y, n);
            stk.pop_back();
        }
    }

    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
        stk.push_back(0);
        dfs(graph, 0, graph.size() - 1);
        return ans;
    }
};

关于我为什么是伞兵这件事(何时使用bfs+状压)

总结就是 数组状压+bfs 只适用于图类型的最短距离。而且一般适用于多源bfs求最短路,或者是用于利用这个状压数组求出答案所在地的具体路径,而不是直接用 vector 的记录方式。

我是伞兵。。明明就很简单的的回溯思路求出所有路径。。我还以为会形成环。。还把它当多源来做了。。最重要的是我竟还以为我自己的写法很高明。。(想了贼久)强行用bfs+状态压缩写了出来。。。 > 但此方法也不是全然毫无用处—可用于有关最短路径问题中加了很多个限制条件的题目,比如限制为走完所有结点的最短路径。又比如计算图中走到某点最短距离的具体经过的路线。这种方法一般就是图的最短路径改编题。

struct node{
    int lable;
    int path;
};

class Solution {
public:
    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
        int n = graph.size();
        const int INF = 1<<n;
        queue<node>Q;
//用memo记录下到达n结点处经过的路径的上一个结点。不仅满足了path防止回头还记录了path的解析方式
        int memo[n][INF];
        memset(memo,0xff,sizeof(memo));
        Q.push({0,1<<0});
        auto func = [&](vector<vector<int>>&res,int path){
            vector<int>t;
            t.emplace_back(n-1);
            int new_path = path;
            for(int i=memo[n-1][path],j=n-1;i!=-1;i=memo[i][new_path]){
                t.emplace_back(i);
                new_path ^=(1<<j);
                j=i;
            }
            reverse(t.begin(),t.end());
            res.emplace_back(t);
        };
   
        vector<vector<int>>res;
        while(!Q.empty()){
            auto new_node = move(Q.front());Q.pop();
            if(new_node.lable==n-1) func(res,new_node.path);
            
            for(auto&& t:graph[new_node.lable]){
                int n_path = new_node.path | 1<<t;
                
                if(memo[t][n_path]!=new_node.lable){
                    memo[t][n_path] = new_node.lable;
                    Q.push({t,n_path});
                }
            }
        }
        return res;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LCA+路径压缩的方式可以用于求解树上的桥,具体实现步骤如下: 1. 对于树上每个节点,记录其在树中的深度(或者高度)以及其父亲节点。 2. 对于每个节点,记录其在树上的最小深度(或最小高度)以及其所在子树中深度最小的节点。 3. 对于每条边(u, v),设u的深度小于v的深度(或者高度),则如果v的子树中没有深度小于u的节点,则(u, v)是桥。 具体的实现过程如下: 首先,我们需要对树进行预处理,求出每个节点的深度以及其父亲节点。可以使用深度优先搜索(DFS)或广度优先搜索(BFS)来实现。在这里我们使用DFS来实现: ```c++ vector<int> adj[MAX_N]; // 树的邻接表 int n; // 树的节点数 int dep[MAX_N], fa[MAX_N]; // dep[i]表示节点i的深度,fa[i]表示节点i的父亲节点 void dfs(int u, int f, int d) { dep[u] = d; fa[u] = f; for (int v : adj[u]) { if (v != f) { dfs(v, u, d + 1); } } } ``` 接下来,我们需要计算每个节点所在子树中深度最小的节点。我们可以使用LCA(最近公共祖先)的方法来实现。具体来说,我们可以使用倍增算法来预处理出每个节点的2^k级祖先,并且在查询LCA时使用路径压缩的方式优化时间复杂度。这里我们不展开讲解LCA和倍增算法的细节,如果你对此感兴趣,可以参考其他资料进行学习。 ```c++ const int MAX_LOG_N = 20; // log2(n)的上取整 int anc[MAX_N][MAX_LOG_N]; // anc[i][j]表示节点i的2^j级祖先 int mn[MAX_N]; // mn[i]表示节点i所在子树中深度最小的节点 void precompute() { // 预处理anc数组 for (int j = 1; j < MAX_LOG_N; j++) { for (int i = 1; i <= n; i++) { if (anc[i][j - 1] != -1) { anc[i][j] = anc[anc[i][j - 1]][j - 1]; } } } // 计算mn数组 for (int i = 1; i <= n; i++) { mn[i] = i; for (int j = 0; (1 << j) <= dep[i]; j++) { if ((dep[i] & (1 << j)) != 0) { mn[i] = min(mn[i], mn[anc[i][j]]); i = anc[i][j]; } } } } ``` 最后,我们可以使用LCA+路径压缩的方式来判断每条边是否为桥。具体来说,对于每条边(u, v),我们需要判断v的子树中是否存在深度小于u的节点。如果存在,则(u, v)不是桥,否则(u, v)是桥。 ```c++ bool is_bridge(int u, int v) { if (dep[u] > dep[v]) swap(u, v); if (mn[v] != u) return true; // 子树中存在深度小于u的节点 return false; // 子树中不存在深度小于u的节点 } ``` 完整代码如下:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值