题目来源于《算法导论》第15章动态规划的习题 15-1,显然建议使用DP
对最小距离大家应该都比较熟悉,比如对非负权值图中有Dijkstra算法,对负权值图中有Ford算法。 最远距离?假设从A到F的最远距离为A->C->D->E->F, 为什么D->E->F一定会是从D到F的最远距离?我在这点证明困扰了很久,直到最近才想明白其中原理。
当重新审视有向(directed) 无回路(acyclic)这两个词时,我才突然想到原来可以这样证明:
依然假设A到F的最远距离为A->C->D->E->F, 现在考虑从D到F:如果存在B,使得D->B->F更远,那么我们直接可以用这段路径代替D->E->F, 这与我们之前的假设相矛盾;如果D经过C再到达F的距离更远,比如D->C->(E)->F, 那就说明存在回路D<->C, 这与条件相矛盾。所以,这里,D->E->F必然是从D到F的最远路径。算法的理论基础终于找到了!
OK,既然最远子路径是问题的必要条件,那么反过来,我们找到每一个点(除了终点以外)到达终点的最远路径,就可以作为整个问题的充分条件。我们需要一个合适的顺序,遍历所有顶点,从终点开始,逐渐向外扩展。问题到了这里就很明显了,采用拓扑序,广度优先的方式遍历。
具体实现中,我用了一个queue存放BFS中覆盖到的顶点。注意在每加入一个新的顶点v时,除了要遍历已计算过的顶点再确定最大的path[v]时,还要反过来考虑对于已经计算过最远距离的顶点u,如果存在权边u->v,那么path[u]也可能需要更新,这一步是最远路径问题相比于最短路经多出来的一步。
建议同学们自己实现一遍整个方法。因为对于图论问题,实现算法会产生很多自己独到的感觉,尤其是一些辅助性的数据结构。以下是我的一份实现:
int longestpath(int** adjMtx, int n, int s, int t){
int* paths = new int[n](); //paths[i] means path from i to t
int* entries = new int[n](); //mark whether vertex has been pushed to queue
int* pnext = new int[n](); //pnext[i] means for longest path (i,t), next vertex
queue<int> vqueue;
vqueue.push(t);
entries[t]=1;
while(!vqueue.empty()){
int v = vqueue.front();
vqueue.pop();
if(v != t){ //update paths[v], while paths[t] = 0
int* edgesv = adjMtx[v];
for(int i=0;i<n;++i){ //edgesv[i] means edge (v,i)
if(i != v && edgesv[i] != 0){
if(paths[v] < edgesv[i] + paths[i]){ //update path(v,t) if it < edge(v,i) + path(i,t)
paths[v] = edgesv[i] + paths[i];
pnext[v] = i;
}
}
}
for(int i=0;i<n;++i){ //update paths[i] if paths[i] < edge(i,v) + paths[v]
if(i != v && paths[i] > 0 && adjMtx[i][v] != 0 && adjMtx[i][v] + paths[v] > paths[i]){
paths[i] = adjMtx[i][v] + paths[v];
pnext[i] = v;
}
}
}
for(int i=0;i<n;++i){ //push i if edge (i,v) occurs
if(i != v && adjMtx[i][v] != 0 && entries[i]==0){
vqueue.push(i);
entries[i]=1;
}
}
}
int vn = s;
printf("the longest path from %d to %d is: %d", s, t, vn);
while(pnext[vn] != 0){
vn = pnext[vn];
printf(" -> %d", vn);
}
printf("\n");
int sum = paths[s];
/*delete paths, entries, pnext*/
return sum;
}另外说下测试数据,下面是我用到的两个图
图一从A到F,最远路径为A->B->C->E->F,长度为10;图二从A到E,最远路径是A->D->B->C-E, 长度为8.
9996



被折叠的 条评论
为什么被折叠?



