[英]Bellman-ford algorithm for one vertex
我有一个任务要做算法,它会找到图中最长的路径。 我将在输入上输入两个数字(N,M)
,表示矩阵的大小和N*M
数字。 它们表示每个顶点的值。 我必须找到从第一个顶点到最后一个的最长路径,我只能向下或向右移动。 所以输入例如:
4 5
1 2 5 1 2
3 2 1 2 1
1 4 3 2 1
3 1 2 2 2
输出是
19
最长的路径包括这些顶点的顺序: 1,3,2,4,3,2,2,2
我使用过Bellman-Ford算法,因为我已将每个顶点的值更改为负值。 但是这个算法对于大量的数据(1000x1000)来说太慢了。 是否有任何选项可以更改此算法以仅查找两个顶点(第一个和最后一个)之间的最大路径,而不是在第一个顶点和每个其他顶点之间的路径? 这是我的代码:
# include <stdio.h>
# include <stdlib.h>
typedef struct {
int source;
int dest;
int weight;
} Edge;
void BellmanFord(Edge edges[], int edgecount, int nodecount, int source)
{
int *distance = malloc(nodecount * sizeof *distance);
int i, j;
distance[source] = -1;
for (i=0; i < nodecount; ++i) {
for (j=0; j < edgecount; ++j) {
if (distance[edges[j].source] < 0) {
int new_distance = distance[edges[j].source] + edges[j].weight;
if (new_distance < distance[edges[j].dest])
distance[edges[j].dest] = new_distance;
}
}
}
printf("%d\n",-distance[nodecount-1]-1);
free(distance);
return;
}
int main(void)
{
Edge *edges;
edges = malloc(2000000*sizeof(Edge));
int n,m,i,k,chodbapomoc,q = 0,p = 0,pamat;
scanf("%d %d",&n,&m);
for(i = 0; i < n*m;i++){ //this is transformation of input to edges
if(i != n*m-1) //list, so i can go only down or right
scanf("%d",&chodbapomoc);
if(p%m != m-1 && p != 0){
edges[q].source = p;
edges[q].dest = p+1;
edges[q++].weight = -chodbapomoc;
}
else if(i == 0){
k = chodbapomoc;
scanf("%d",&chodbapomoc);
edges[q].source = p;
edges[q].dest = p+1;
edges[q++].weight = -chodbapomoc-k;
}
if(p > m-1 && p != m){
edges[q].source = p-m;
edges[q].dest = p;
edges[q++].weight = -pamat;
}
else if(i == m-1){
edges[q].source = 0;
edges[q].dest = m;
edges[q++].weight = -chodbapomoc-k;
}
pamat = chodbapomoc;
p++;
}
BellmanFord(edges, q, n*m, 0);
return 0;
}
或者还有其他选择,比这更快,找到DAG中最长的路径? 而且,有什么办法可以记住最大路径中的哪些版本?
谢谢你的回复
有一种算法可以改进:在您的情况下,复杂度可以是O(N)
,其中N
是图像上的点数。
Bellman-Ford算法旨在处理任何加权有向图 。 它在最坏情况下的复杂性是O(mn)
,其中n
是节点数, m
是边数。
请注意,您的有向图是非循环的 :图中没有定向循环。 从任何一点来看,都有“未来点”(你将来访问它们)和“过去点”(你可能来自那里)。 Kahn算法使用此属性来执行拓扑排序 。 最后,这种有向图中的最短路径算法依赖于拓扑排序,总复杂度为O(n+m)
!
由于您正在处理数组,并且由于只允许top-> bottom和left-> right移动,因此很容易找到拓扑排序。 只需从左到右依次访问这些线路,并在途中更新最大距离! 在程序结束时,图像填充距离源的最大距离。 有四种不同的情况:
i==0 && j==0
:这是源点。 它的最大距离等于它的重量。 i==0 && j!=0
:这是第一行中的一个点。 到达那里的唯一方法是向左走。 所以它的最大距离是从线的起点开始的权重之和。 i!=0 && j==0
:这是第一列中的一个点。 到达那里的唯一方法是下降。 因此,它的最大距离是来自列的开始的权重之和。 i!=0 && j!=0
:一般情况。 请注意,点(i-1,j)
和(i,j-1)
已经存在距离源的最大距离。 到点(i,j)
的最大距离是这两个距离中的最大距离加上点(i,j)
的权重。 最终数组存储距离源的最大距离。 另一个数组存储路径信息(最大值来自哪里?)。
第一行 :
1 3 8 9 11 s l l l l
第二行 :
1 3 8 9 11 s l l l l
4 6 9 11 12 u l u lu lu
第三线:
1 3 8 9 11 s l l l l
4 6 9 11 12 u l u lu lu
5 10 13 15 16 u u l l l
最后一行:
1 3 8 9 11 s l l l l
4 6 9 11 12 u l u lu lu
5 10 13 15 16 u u l l l
8 11 15 17 19 u u u lu l
由于右侧的阵列,可以轻松地检索2条最长的路径。 该阵列只读一次:这个技巧可以减少几个数量级的计算时间,它仍然为您提供精确的解决方案!
如果它仍然太慢并且如果近似解是足够的,那么看一下模拟退火 。 探索的空间将是诸如DDRRRDR
路径,其中n-1
D
(向下)和m-1
R
(向右)。 能量将是-distance(DDRRRDR)
,一个小修改将交换一个D和一个R.
下面是gcc main.c -o main -Wall
编译的精确解决方案(非退火)的示例代码。 编辑:它现在还打印一个最大长度的路径。
# include <stdio.h>
# include <stdlib.h>
typedef enum {S,L,U,LU} Direction;
int main(void)
{
FILE * pFile;
int n=1,m=1,i,j;
int* image;
pFile = fopen ("image.txt","r");
if (pFile!=NULL)
{
if(fscanf(pFile,"%d%d",&n,&m)!=2){printf("read failed\n");exit(1);}
image=malloc(n*m*sizeof(int));
if(image==NULL){printf("malloc failed\n");exit(1);}
for(i=0;i<n*m;i++){
if(fscanf(pFile,"%d",&image[i])!=1){printf("read failed %d\n",i);exit(1);}
}
fclose (pFile);
}else{printf("file open failed\n");exit(1);}
Direction* directions=malloc(n*m*sizeof(Direction));
for(i=0;i<n;i++){
for(j=0;j<m;j++){
//getting the direction of max
if(i==0 && j==0){
directions[i*m+j]=S;
}
if(j==0 && i>0){
directions[i*m+j]=U;
}
if(j>0 && i==0){
directions[i*m+j]=L;
}
if(j>0 && i>0){
if(image[i*m+(j-1)]>image[(i-1)*m+j]){
directions[i*m+j]=L;
}else{
if(image[i*m+(j-1)]<image[(i-1)*m+j]){
directions[i*m+j]=U;
}else{
directions[i*m+j]=LU;
}
}
}
//setting the new value of image[i*m+j]
if(directions[i*m+j]==L){
image[i*m+j]+=image[i*m+j-1];
}else{
if(directions[i*m+j]==U || directions[i*m+j]==LU){
image[i*m+j]+=image[(i-1)*m+j];
}
}
}
}
printf("max distance is %d\n",image[n*m-1]);
printf("A path from the end is\n");
char path[n+m-1];
path[n+m-2]='\0';
int cnt=n+m-3;
i=n-1;
j=m-1;
printf("(%d %d)\n",i,j);
while(i!=0 || j!=0){
if(directions[i*m+j]==LU){printf("many possible max path. going left\n");}
if(directions[i*m+j]==U){
printf("D ");
path[cnt]='D';
i--;
}else{
printf("R ");
path[cnt]='R';
j--;
}
cnt--;
printf("(%d %d)\n",i,j);
}
printf("A max path is %s\n",path);
free(directions);
free(image);
return 0;
}
图像在文件image.txt
中提供,如下所示:
4 5
1 2 5 1 2
3 2 1 2 1
1 4 3 2 1
3 1 2 2 2
AB Khan, 大型网络拓扑排序 ACM第5卷第11期通信,1962年11月第558-562页:10.1145 / 368996.369025
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.