首页 文章

算法枚举所有可能的路径

提问于
浏览
7

请考虑以下图表:

alt text

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

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

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

我写了一个深度优先搜索(DFS)算法,但问题是当你在2个节点之间有多条边(比如上面的边缘F3和F5)时,我不知道如何处理它 . 我的算法只返回路径 A C D EA C E ,而不是其他路径 . 在 A B C 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

2 回答

  • 3

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

    但问题是,当你有两个节点之间的多个边(如上面的边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;
    }
    
  • 0

    我的猜测是,如果你说的话,重复的路径问题也会出现

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

    我认为你的“我们以前来过这里”测试不应只看当前节点,而是看你到达那里的路径 . 所以不要检查“O”,检查“JMNO”和“JKLO” .

相关问题