views:

76

answers:

2

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
+2  A: 

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;
}
Nikita Rybak
Thanks, DFS with backtracking worked following your suggestion above.
dcp
A: 

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".

djna
@djna - Good point, but I believe the backtracking approach suggested by Nikita handles that. When it gets to T, it will "backtrack" all the way back out to J (marking the nodes as un-visited along the way), then it will recurse down the path again to get your second path.
dcp