在不包含循环的有向图中,如何求得两个指定点间的最长路径?

Zark 2003-06-10 12:29:04
在不包含循环的有向图中,如何求得两个指定点间的最长路径? 请或给出伪码,或是C/C++/JAVA代码.既使是大致思路也可以.

预致谢意!
...全文
307 点赞 收藏 15
写回复
15 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
Zark 2003-06-12
非常感谢,未能及时回话是因为昨天太忙,然后又要读你的代码,又要研究另一种算法,所以回复迟了.关于你的修改,我再看看,我在运行运程中没有遇到你所说的问题.我想可能是因为我的问题实际上不是任两点的路径,而是在图中只有一个入度为零的顶点和一个出度为零的顶点,我实际是在求这个两点的路径.我在考虑"关键路径"法,好像用拓扑有序这种方法也能解决,不过好像也是O(n^2).
回复
孩皮妞野 2003-06-12
Zark, 怎么一直没回话,这里有个小bug, 可是我用完了3次,不能再发贴,把我急得。

unsigned max_path_length(unsigned i, unsigned j)
{
if(i==j) return 0; // 递归退出条件

if( max_paths[i][j] != unknown)
return max_paths[i][j]; //已经计算,直接取用计算结果

unsigned max_path = nopath;
unsigned next_vert; // 下一个节点
for(unsigned k=0; k<max_paths.size(); ++k){
if( adj_matrix[i][k]==1 ){ // k是i的后继
unsigned len = max_path_length(k,j);
// 这里做了局部修改,否则可能出现错误
// 当max_path不为nopath而len为nopath时会出现不合理的赋值动作。
if( len!=nopath && (max_path==nopath || max_path<len ) )
max_path = len, next_vert=k;
}
}
if(max_path != nopath)
++max_path;
max_paths[i][j]= max_path;
max_path_next_vert[i][j] = next_vert;
return max_path;
}
回复
Zark 2003-06-12
非常感谢!经验证,结果正确。

回复
孩皮妞野 2003-06-11
Zark(金陵五月) ( ) ,

是动态规划法,复杂度好像是O(n^2), 我想不会达不到你的精度要求吧。

建议看一下动态规划法的介绍。 如果有时间我会把这个题目写出来。算法出来了编程没有什么难度。
回复
Zark 2003-06-11
如果我没有理解错的,你的算法实际上是从结点A到结点C进行多次搜索,并把每次搜索的路径都记录下来,比如A->B->C, A->D->B->C,然后再从其中选择最长的一组.

这是我最原始的想法,类似于穷举法.但我仿佛觉得应该还有更好的算法,不必进行穷举.众所周知,如果求A到C的最短路径,那么穷举法是完全可以成功的,但不是最好的,可以通过类似于树的广度搜索的方法,对A查B和D,发觉没有到达C,分别从B和D出度搜索C,结果是B可以到达C,那么这条路一定是最短的,这样不用穷举即可得到最短路径.

不知老兄对此有无高见,敬请赐教.
回复
nKannan 2003-06-11
图的遍历问题。
使用一个队列,开始只有点A。
将A可以到达的点放入队列,BD。
查下一个点B,将B可以到达的点入队列,现在有DC。
反复查队列,直到为空。
记录其中到达C点所经历的最长路径。

这是实现最简单但是效率差的方法。
回复
孩皮妞野 2003-06-11
记顶点数为N, 用一个N*N的矩阵来存储从每一顶点到所有其他顶点的最大长度。

这个矩阵可以一次求出来,反复使用,只要基础图没有改变。

如有问题,欢迎讨论。
回复
孩皮妞野 2003-06-11
这是DP算法,是多项式复杂度吧。
回复
孩皮妞野 2003-06-11
我刚才理解错了。

把边的权都置为1。

记从S到D的最长路径的长度为MAXsd,

从S可以直接到达的节点集合为{A,B,C,D...}

则MAXsd = 1+ max{ MAXad, MAXbd, MAXcd, MAXdd...}


在求最长路径长度的同时可以求出最长路径。
回复
Zark 2003-06-11
多谢回复.

1.我的书上没有这种算法,如有可能请告诉我是哪一本书.
2.这个图没有那么复杂,边是没有权的.举例来说,
A----->B----->C
| ^|
---->D---

从A->C,则要找出A->D->B->C,而不是A->B->C.我不知道如何使用ALNG的方法.

回复
孩皮妞野 2003-06-11
哦,是图,可以用最小代价生成树的算法[prim/kruskal]. 道理如上。
回复
孩皮妞野 2003-06-11
你是指不包含同一节点两次的最长路径吧?

最长和最短,看你怎么看。比如对边的权取相反数,或者用某个大数减去权得到新权[这样可以保证权仍为正数],这样用新权得到的最短路径,就是原来权意义上的最长路径。

所以dijikstra算法就可以了。
回复
brucegong 2003-06-11





数据结构上不是有现成的算法吗?




回复
孩皮妞野 2003-06-11
另一组稍复杂的数据
C:\ type input.txt
0 9
13
0 1 0 1 0 0 0 0 0 0 0 0 0
0 0 1 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 0 0 0 0
0 1 0 0 1 0 0 0 0 0 0 0 0
0 0 1 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 1 0 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 1 0 0 1
0 0 0 0 0 0 0 0 0 1 0 0 0


c:\ lpath <input.txt
11
0 3 1 4 2 5 6 8 10 11 12 9

这个结果也是正确的。

该算法的复杂度为O(n^2).

回复
孩皮妞野 2003-06-11
局部还可以优化。在CB6下运行通过,以你的样本数据测试通过。没做进一步测试。

必须保证输入的矩阵没有回路,否则可能陷入死循环[?]。


// 解Zark最长路径问题的DP算法。
// ALNG 2003-6-11
//
//
//---------------------------------------------------------------------------
// 输入文件的格式
// 源 目标
// 顶点数
// 邻接矩阵
//
// 示例输入文件input.txt, 就是zark给出的示例图
// 0 2
// 4
// 0 1 0 1
// 0 0 1 0
// 0 0 0 0
// 0 1 0 0
//
// 输出
// 路径长度
// 路径
//
// 示例输出
// 3
// 0 3 2


#include <iostream>
#include <vector>

typedef std::vector< std::vector<unsigned> > matrix;

matrix max_paths; // 存储最长路径长度的矩阵
matrix max_path_next_vert; // 用于构造出最长路径
matrix adj_matrix; // 邻接矩阵

// max_paths中用到的特殊值
const unsigned unknown = 0, // 表明两个顶点间的最长路径长度未知
nopath = unsigned(-1); // 表明两个顶点间的最长路径长度未知


// 从标准输入中读入邻接矩阵
//
// input:
// n[in]: count of vertices
//
// remark: this function modifies global variable max_paths and adj_matrix
void read_adj_matrix(unsigned n)
{
// read adjacent matrix from stand input
adj_matrix.resize(n);
for(unsigned i=0; i<n; ++i){
adj_matrix[i].resize(n);
for(unsigned j=0; j<n; ++j){
std::cin>>adj_matrix[i][j];
}
}
// initialize max_paths, set all of its elements to unknown
max_paths.resize(n);
max_path_next_vert.resize(n);
for(unsigned i=0; i<n; ++i){
max_paths[i].resize(n);
max_path_next_vert[i].resize(n);
for(unsigned j=0; j<n; ++j)
max_paths[i][j] = unknown;
}
}

// 取得从i到j的最大路径的长度
//
// 返回 unsigned(-1)表示没有从i到j的路径
// 其它:
unsigned max_path_length(unsigned i, unsigned j)
{
if(i==j) return 0; // 递归退出条件

if( max_paths[i][j] != unknown)
return max_paths[i][j]; //已经计算,直接取用计算结果

unsigned max_path = nopath;
unsigned next_vert; // 下一个节点
for(unsigned k=0; k<max_paths.size(); ++k){
if( adj_matrix[i][k]==1 ){ // k是i的后继
unsigned len = max_path_length(k,j);
if( max_path==nopath || max_path<len )
max_path = len, next_vert=k;
}
}
if(max_path != nopath)
++max_path;
max_paths[i][j]= max_path;
max_path_next_vert[i][j] = next_vert;
return max_path;
}

void longest_path(unsigned i, unsigned j)
{
unsigned len;
if( (len=max_path_length(i,j) ) != nopath){
std::cout<<len<<std::endl<<i;
for(unsigned k=max_path_next_vert[i][j]; k!=j;i=k,k=max_path_next_vert[i][j])
std::cout<<' '<<k;
std::cout<<' '<<j<<std::endl;
}else
std::cout<<"there is no path between "<<i<<" and "<<j<<std::endl;

}


#pragma argsused
int main(int argc, char* argv[])
{
using std::cin;
using std::cout;
using std::endl;
unsigned src, dst, n;
cin>>src>>dst>>n;

read_adj_matrix(n);

longest_path(src,dst);

return 0;
}
//---------------------------------------------------------------------------
回复
发动态
发帖子
C语言
创建于2007-09-28

6.3w+

社区成员

C语言相关问题讨论
申请成为版主
社区公告
暂无公告