我想在有向图中找到顶点1和顶点n之间的路径数。该图包含循环。如果顶点1和顶点n之间的任何路径都有一个循环,那么结果应该是“INFINITE PATHS”,否则就是路径数。这就是我的尝试。
1)我修改了DFS算法,它从节点1开始,所有节点最初都是白色的。
2)如果访问灰色节点,则表示存在循环。
3)记录访问顶点的路径数组用于循环回溯节点。
4)对于循环中的每个节点,未修改的DFS用于搜索该节点的第n个顶点。
5)如果DFS从任何一个节点成功,那么在顶点1和顶点n之间的路径中存在一个循环,所以它返回,否则算法继续计算路径数。
c ++实现
#include <stdio.h>
#include <malloc.h>
#include <vector>
#include <algorithm>
#define WHITE 0
#define GRAY 1
#define BLACK 2
using namespace std;
typedef struct vertexstruct{
int color;
vector<int> edgelist;
}vertex;
int ordinarydfs(int v,vertex *vertices,int numvertices){
if(v == numvertices){
return 1;
}
if(vertices[v].color == WHITE){
vertices[v].color = GRAY;
for(int i=0;i<vertices[v].edgelist.size();i++){
int x = ordinarydfs(vertices[v].edgelist[i],vertices,numvertices);
if(x ==1) return 1;
}
vertices[v].color = BLACK;
}
return 0;
}
int checkcycle(int v,vector<int>path,vertex *vertices,int numvertices){
vector<int>::iterator it;
vector<int>::iterator x;
it = find (path.begin(), path.end(), v);
for(x =it;x<path.end();x++)
vertices[*x].color = WHITE;
for(x =it;x<path.end();x++){
if(ordinarydfs(*x,vertices,numvertices))
return 1;
}
for(x =it;x<path.end();x++)
vertices[*x].color = GRAY;
return 0;
}
long dfs(int v,vertex *vertices,int numvertices,vector<int> path){
long paths = 0;
if(v == numvertices){
return 1;
}
if(vertices[v].color == GRAY) {
if(checkcycle(v,path,vertices,numvertices)) return -1;
}
if(vertices[v].color == WHITE){
vertices[v].color = GRAY;
path.push_back(v);
for(int i=0;i<vertices[v].edgelist.size();i++){
long x = dfs(vertices[v].edgelist[i],vertices,numvertices,path);
vertices[vertices[v].edgelist[i]].color = WHITE;
if(x == -1) return -1;
paths += x;
}
vertices[v].color = BLACK;
}
return paths % 1000000000;
}
void numpaths(int numvertices,int numedges,vertex *vertices){
vector<int> path;
long numpaths = 0;
numpaths = dfs(1,vertices,numvertices,path);
if(numpaths == -1)
printf("INFINITE PATHS\n");
else
printf("%ld\n",numpaths);
}
int main(){
int edgecount=0;
int numverts,numedges;
fscanf(stdin,"%d %d",&numverts,&numedges);
vertex verts[numverts+1];
for(int i =1;i<=numverts;i++)
verts[i].color = WHITE;
edgecount = 0;
int a,b;
while(edgecount < numedges){
scanf("%d %d",&a,&b);
verts[a].edgelist.push_back(b);
edgecount++;
}
numpaths(numverts,numedges,verts);
}
对于大型图表,该算法太慢。这个问题有正确的方法吗?分享你的意见。谢谢。
答案 0 :(得分:3)
完全不同的方法是将图表表示为邻接矩阵A [i] [j] = 1 if(i,j)是边,否则为0。然后A ^ i [s] [t]计算从s到t的路径数,它们的长度为i(这可以通过归纳证明。想想A ^ 2代表什么)。对于幂1 .. | V |,求和A [s] [t],并且所有可能的路径长度都达到| V |。要检查是否存在循环,请参阅(通过再次乘以矩阵)长度为| V | + 1,...,2 | V |的路径。存在。
这有帮助吗?
答案 1 :(得分:2)
您没有使用任何缓存,因此dfs()
和ordinarydfs()
将被多次调用。用于检查周期的path
向量是多余的,因为您可以通过其颜色判断顶点是否在当前路径中。也没有必要进行特殊检查以查看循环是否连接到最后一个顶点,因为您已经计算了最后一个顶点有多少条路径。
我将整个BFS放在一个方法中并重写了大部分代码:
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
class Vertex;
const int WHITE = 0, GRAY = 1, BLACK = 2;
// WHITE: unseen, GRAY: in the current path, BLACK: finished (num paths known)
const int CYCLE = -1;
class Vertex
{
public:
long long paths;
int color;
bool hasLoop;
vector<Vertex *> nbrs;
Vertex();
long long countPaths();
};
int main()
{
int numverts, numedges;
Vertex** verts; // dynamically-allocated array of Vertex*
scanf("%d %d", &numverts, &numedges);
verts = new Vertex*[numverts+1];
for (int i = 0; i < numverts + 1; i++) verts[i] = new Vertex();
for (int i = 0; i < numedges; i++)
{
int a, b;
scanf("%d %d", &a, &b);
verts[a]->nbrs.push_back(verts[b]);
}
verts[numverts]->paths = 1; // the base case
long long numPaths = verts[1]->countPaths();
// free dynamic memory, set pointers to NULL to prevent accidents
for (int i = 0; i < numverts; i++) { delete verts[i]; verts[i] = NULL; }
delete[] verts; verts = NULL;
if (numPaths == CYCLE) printf("INFINITE PATHS\n");
else printf("%lld\n", numPaths);
return 0;
}
Vertex::Vertex()
{
paths = 0;
color = WHITE;
hasLoop = false;
}
long long Vertex::countPaths()
{
// sets this->paths to the number of paths, or CYCLE (-1) for cycles
// returns this->paths
if (color == BLACK)
{
// paths already calculated, no need to do anything
}
else if (color == GRAY)
{
// detected a loop
hasLoop = true;
}
else if (color == WHITE)
{
// recursively find all the paths from the neighbours
color = GRAY;
for (unsigned int i = 0; i < nbrs.size(); i++)
{
nbrs[i]->countPaths();
if (nbrs[i]->paths == CYCLE)
{
// found cycle, no point continuing
paths = CYCLE;
break;
}
paths += nbrs[i]->paths;
}
color = BLACK;
// check if some other node found a cycle to 'this'
if (hasLoop && paths > 0) paths = CYCLE;
}
return paths;
}
答案 2 :(得分:1)
我们可以通过以下步骤解决此问题:
(如果仍然需要,我会尝试稍后发布一个实现)
答案 3 :(得分:0)
我试过这个,速度很快,但我不确定它是否正确。到达黑色节点时,意味着您已经计算了该节点的路径,因此我在节点结构中存储了路径数并使用了它。
#include <stdio.h>
#include <malloc.h>
#include <vector>
#include <algorithm>
#define WHITE 0
#define GRAY 1
#define BLACK 2
using namespace std;
typedef struct vertexstruct{
int color;
vector<int> edgelist;
long paths;
}vertex;
int cyclenode;
int throughend;
long dfs(int v,vertex *vertices,int numvertices){
long paths = 0;
if(vertices[v].color == BLACK) return vertices[v].paths;
if(vertices[v].color == GRAY) {
if(cyclenode == 0)cyclenode = v;
}
if(vertices[v].color == WHITE){
vertices[v].color = GRAY;
for(int i=0;i<vertices[v].edgelist.size();i++){
long x = dfs(vertices[v].edgelist[i],vertices,numvertices);
if(cyclenode) vertices[v].cycle=1;
if(x == -1) return -1;
paths += x;
}
if(cyclenode && paths >0) return -1;
if(v == numvertices){
if(cyclenode) {
return -1;
}
throughend = 0;
vertices[v].paths = 1;
return 1;
}
if(cyclenode ==v && throughend == 0) cyclenode = 0;
vertices[v].color = BLACK;
vertices[v].paths = paths % 1000000000;
}
return paths % 1000000000;
}
void numpaths(int numvertices,int numedges,vertex *vertices){
long numpaths = 0;
cyclenode = 0;
throughend =0;
numpaths = dfs(1,vertices,numvertices);
if(numpaths == -1)
printf("INFINITE PATHS\n");
else
printf("%ld\n",numpaths);
}
int main(){
int edgecount=0;
int numverts,numedges;
fscanf(stdin,"%d %d",&numverts,&numedges);
vertex verts[numverts+1];
for(int i =1;i<=numverts;i++){
verts[i].color = WHITE;
verts[i].paths = 0;
}
edgecount = 0;
int a,b;
while(edgecount < numedges){
scanf("%d %d",&a,&b);
verts[a].edgelist.push_back(b);
edgecount++;
}
numpaths(numverts,numedges,verts);
}