枚举所有可能路径的算法

发布于 2024-09-27 02:35:44 字数 2874 浏览 7 评论 0原文

考虑下图:

alt text

我正在尝试找到一种方法来枚举从源节点到目标节点。例如,从 A 到 E,我们有以下可能的路径:

A B C D E
A B C E
A C D E
A C E

请注意,对于 ACDE,实际上有 2 条路径,因为其中一条路径使用边 F3,另一条路径使用边 F5。另外,由于 A 和 C 之间存在循环,因此最终可能会得到无数条路径,但出于此目的,我只对从源到目标的路径上没有重复节点的路径感兴趣。

我编写了一个深度优先搜索 (DFS) 算法,但问题是,当 2 个节点之间有多个边(如上面的边 F3 和 F5)时,我不知道如何处理它。我的算法仅返回路径 ACD EAC E,而不返回其他路径。对于 ABC E ,我理解原因,因为它从 A 开始,然后转到 C 并构建这些路径,但是当 DFS 返回到节点 B 时,它会尝试转到C,但 C 已被访问过,因此停止。

不管怎样,我只是想知道是否有办法做到这一点,或者也许这是 NP 完全的。

如果您想查看我的 DFS,代码如下(很抱歉宏滥用,我在竞赛编程中使用这些,所以这是一个习惯)。

#include <algorithm>
#include <numeric>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cassert>
#include <cmath>
#include <complex>
#include <stack>
#include "time.h"
using namespace std;
#define SZ(x) (int)x.size()
#define FOR(i,x,y) for(int i=(int)(x);i<=(int)(y);++i)
#define REP(i,n) FOR(i,0,n-1)
#define FORD(i,x,y) for(int i=(int)(x);i>=(int)(y);--i)
#define ALL(a) (a).begin(),(a).end()
#define FORE(i,t) for(i=t.begin();i!=t.end();++i)
typedef vector<int> VI;
typedef vector<string> VS;
typedef vector<bool> VB;
typedef vector<double> VD;
typedef deque<int> DI;
typedef deque<string> DS;
typedef long long i64;
#define PI 3.14159265358979323
#define DEGTORAD(x) (double)x * 3.14159265358979323846264338327950288 / 180.0
#define RADTODEG(x) (double)x * 180 / 3.14159265358979323846264338327950288
#define prt if(1)printf
template <typename T> string tostr(const T& t) { ostringstream os; os<<t; return os.str(); } 

typedef pair< char, char > PCC;
map< PCC, int > adj;
map< char, bool > vis;

vector< char > path;

void dfs(char at) {
  if (at == 'E') {
    REP(i,SZ(path)) {
      if (i != 0)
        cout<<",";
      cout<<path[i];
    }
    cout<<",E"<<endl;
    return;
  }
  if (vis[at])
    return;
  vis[at] = true;
  map< PCC, int >::iterator it;
  FORE(it,adj) {
    if (it->first.first == at) {
      path.push_back(it->first.first);
      dfs(it->first.second);
      path.erase(path.end()-1);
    }
  }
}


int main() {
  adj[make_pair('A','B')] = 1;
  adj[make_pair('A','C')] = 1;
  adj[make_pair('C','D')] = 1;
  adj[make_pair('D','E')] = 1;
  adj[make_pair('E','I')] = 1;
  adj[make_pair('C','F')] = 1;
  adj[make_pair('F','G')] = 1;
  adj[make_pair('F','H')] = 1;
  adj[make_pair('C','E')] = 1;
  dfs('A');
  return 0;
}

输出:

---------- Capture Output ----------
A,C,D,E
A,C,E

Consider the following graph:

alt text

I'm trying to find a way to enumerate all possible paths from a source node to a target node. For example, from A to E, we have the following possible paths:

A B C D E
A B C E
A C D E
A C E

Note that for A C D E, there are actually 2 paths, since one of the paths uses edge F3 and the other uses edge F5. Also, since there's a cycle between A and C, you could end up with an infinite number of paths, but for the purposes of this I'm only interested in paths in which no node is repeated on the path from source to target.

I wrote a depth first search (DFS) algorithm, but the problem is that when you have multiple edges between 2 nodes (like edges F3 and F5 above) I'm not sure how to handle it. My algorithm only brings back paths A C D E and A C E, not the other paths. In the case of A B C E, I understand the reason, because it starts at A and then goes to C and builds those paths, but when the DFS gets back to node B, it then tries to go to C, but C has already been visited so it stops.

Anyway, I just wondered if there was a way to do this, or maybe this is NP-complete.

In case you'd like to see my DFS, code is below (sorry for the macro abuse, I use these in contest programming so it's a bit of a habit).

#include <algorithm>
#include <numeric>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cassert>
#include <cmath>
#include <complex>
#include <stack>
#include "time.h"
using namespace std;
#define SZ(x) (int)x.size()
#define FOR(i,x,y) for(int i=(int)(x);i<=(int)(y);++i)
#define REP(i,n) FOR(i,0,n-1)
#define FORD(i,x,y) for(int i=(int)(x);i>=(int)(y);--i)
#define ALL(a) (a).begin(),(a).end()
#define FORE(i,t) for(i=t.begin();i!=t.end();++i)
typedef vector<int> VI;
typedef vector<string> VS;
typedef vector<bool> VB;
typedef vector<double> VD;
typedef deque<int> DI;
typedef deque<string> DS;
typedef long long i64;
#define PI 3.14159265358979323
#define DEGTORAD(x) (double)x * 3.14159265358979323846264338327950288 / 180.0
#define RADTODEG(x) (double)x * 180 / 3.14159265358979323846264338327950288
#define prt if(1)printf
template <typename T> string tostr(const T& t) { ostringstream os; os<<t; return os.str(); } 

typedef pair< char, char > PCC;
map< PCC, int > adj;
map< char, bool > vis;

vector< char > path;

void dfs(char at) {
  if (at == 'E') {
    REP(i,SZ(path)) {
      if (i != 0)
        cout<<",";
      cout<<path[i];
    }
    cout<<",E"<<endl;
    return;
  }
  if (vis[at])
    return;
  vis[at] = true;
  map< PCC, int >::iterator it;
  FORE(it,adj) {
    if (it->first.first == at) {
      path.push_back(it->first.first);
      dfs(it->first.second);
      path.erase(path.end()-1);
    }
  }
}


int main() {
  adj[make_pair('A','B')] = 1;
  adj[make_pair('A','C')] = 1;
  adj[make_pair('C','D')] = 1;
  adj[make_pair('D','E')] = 1;
  adj[make_pair('E','I')] = 1;
  adj[make_pair('C','F')] = 1;
  adj[make_pair('F','G')] = 1;
  adj[make_pair('F','H')] = 1;
  adj[make_pair('C','E')] = 1;
  dfs('A');
  return 0;
}

Output:

---------- Capture Output ----------
A,C,D,E
A,C,E

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

转角预定愛 2024-10-04 02:35:44

无论如何,我只是想知道是否有办法做到这一点,或者这可能是 NP 完全的。
我不相信你可以在多项式时间内输出 n! 条可能的路径。如果每个节点都连接到其他节点,这就是您可能会得到的结果。

但问题是,当 2 个节点之间有多个边时(如上面的边 F3 和 F5)
那么,您希望将边 F3F5 视为相同,对吧?那么,在调用 dfs 之前删除重复的边可能是最简单的选择。

因为它从 A 开始,然后转到 C 并构建这些路径,但是当 DFS 返回到节点 B 时,它会尝试转到 C,但 C 已经被访问过,所以它停止了。
好吧,那么我们将 C 标记为未再次访问。

void dfs(char at) {
    vis[at] = true;

    // do stuff with 'at'

    vis[at] = false;
}

Anyway, I just wondered if there was a way to do this, or maybe this is NP-complete.
I don't believe you can output n! possible paths in polynomial time. And that's how may you get if each node is connected to each other node.

but the problem is that when you have multiple edges between 2 nodes (like edges F3 and F5 above)
So, you want to consider edges F3 and F5 to be the same, right? It's probably the simplest option to just remove duplicate edges before you call dfs, then.

because it starts at A and then goes to C and builds those paths, but when the DFS gets back to node B, it then tries to go to C, but C has already been visited so it stops.
Well, let's mark C as not visited again, then.

void dfs(char at) {
    vis[at] = true;

    // do stuff with 'at'

    vis[at] = false;
}
忆离笙 2024-10-04 02:35:44

以下是使用 BFS 实现此操作的方法:以下 (python) 函数(使用递归路径查找修改的 BFS /em> 两个节点之间的函数)可用于查找无环图中两个节点之间的所有可能路径

from collections import defaultdict

# modified BFS
def find_all_parents(G, s):
    Q = [s]
    parents = defaultdict(set)
    while len(Q) != 0:
        v = Q[0]
        Q.pop(0)
        for w in G.get(v, []):
            parents[w].add(v)
            Q.append(w) 
    return parents

# recursive path-finding function (assumes that there exists a path in G from a to b)   
def find_all_paths(parents, a, b): 
    return [a] if a == b else [y + b for x in list(parents[b]) for y in find_all_paths(parents, a, x)]

例如,使用下面的图 (DAG) G(通过删除有向给定图中的循环多边),

G = {'A':['B','C'], 'B':['C'], 'C':['D', 'E', 'F'], 'D':['E'], 'E':['I'], 'F':['G', 'H']}

如果我们想要找到节点'A''E之间的所有路径' (使用上面定义的函数 find_all_paths(find_all_parents(G, 'A'), 'A', 'E')),它将根据需要返回以下路径:

# ACDE
# ABCDE
# ACE
# ABCE

enter图片描述在这里

Here is how you can do it with BFS: the following (python) functions (modified BFS with a recursive path-finding function between two nodes) can be used to find all possible paths between two nodes in an acyclic graph:

from collections import defaultdict

# modified BFS
def find_all_parents(G, s):
    Q = [s]
    parents = defaultdict(set)
    while len(Q) != 0:
        v = Q[0]
        Q.pop(0)
        for w in G.get(v, []):
            parents[w].add(v)
            Q.append(w) 
    return parents

# recursive path-finding function (assumes that there exists a path in G from a to b)   
def find_all_paths(parents, a, b): 
    return [a] if a == b else [y + b for x in list(parents[b]) for y in find_all_paths(parents, a, x)]

For example, with the following graph (DAG) G (by removing the directed cycles and multi-edges from the given graph), given by

G = {'A':['B','C'], 'B':['C'], 'C':['D', 'E', 'F'], 'D':['E'], 'E':['I'], 'F':['G', 'H']}

if we want to find all paths between the nodes 'A' and 'E' (using the above-defined functions as find_all_paths(find_all_parents(G, 'A'), 'A', 'E')), it will return the following paths, as desired:

# ACDE
# ABCDE
# ACE
# ABCE

enter image description here

臻嫒无言 2024-10-04 02:35:44

我的猜测是,如果您说

J->K->L->O->T
J->M->N->O->T

我认为您的“我们以前来过这里吗”测试不应该只查看当前节点,而应该查看您到达那里的路径,那么重复路径问题也会显现出来。因此,不要检查“O”,而是检查“JMNO”和“JKLO”。

My guess is that the duplicate path problem will also manifest if you have say

J->K->L->O->T
J->M->N->O->T

I think your "have we been here before" test should not just look at the current node, but the path by which you got there. So don't check for "O", check for "JMNO" and "JKLO".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文