Всегда ли алгоритм Беллмана-Форда может обнаружить отрицательный круг во взвешенном орграфе или нет?

Вот мой код:

#include<stdio.h>
#include<stdlib.h>

#define inf 99999999

#define vertex 5
#define edge 6

int main(){
    int dis[vertex]={
        0,inf,inf,inf,inf
    };
    int bak[vertex];
    int u[edge],v[edge],w[edge];
    int i,
        k,
        check = 0,
        flag = 0,
        count = 0;
    for(i = 0 ;i<edge;i++){
        scanf("%d %d %d\n",&u[i],&v[i],&w[i]);
    }

    // test if data is received correctly
    for(i = 0 ; i<edge;i++){
        printf("%d %d %d\n",u[i],v[i],w[i]);
    }
    //test_end

    for(k = 0 ;k<vertex-1;k++){   // relax at most vertex-1 time
        count ++;

        /* check = 0; */
        /* for(i = 0 ;i<vertex ;i++){ */
            /* bak[i] = dis[i]; */
        /* } */

        for(i = 0 ;i<edge;i++){
            if(dis[v[i]] > dis[u[i]] + w[i]){
                dis[v[i]] = dis[u[i]] + w[i];
            }
        }

        /* for(i = 0;i<vertex;i++){ */
            /* if(bak[i] != dis[i]){ */
                /* check = 1; */
                /* break; */
            /* } */
        /* } */
        /* if(check == 0){ */
            /* break; */
        /* } */
    }

    // test if have negative circle
    for(i = 0 ; i< edge ;i++){   
        if(dis[v[i]] > dis[u[i]] + w[i])
            flag = 1;
    }
    //test_end 

    if(flag == 1){
        printf("Have circle\n");
    }
    else{
        printf("No circle\n");

        for(i = 0 ; i< vertex;i++){
            printf("%d ",dis[i]);
        }
    }

    printf("\ncount = %d \n",count);

    return 0;
}

И вот мои тестовые данные:

1 2 2
0 1 -3
0 4 5
3 4 2
2 3 3
3 1 -3

И результат на моем компьютере:

1 2 2
0 1 -3
0 4 5
3 4 2
2 3 3
3 1 -3
No circle
0 -3 -1 2 4
count = 4

Но у этого взвешенного орграфа есть отрицательный круг.

Я неправильно понимаю концепцию отрицательного круга. То, что я сказал выше, было чушью. Этот тестовый взвешенный орграф не содержит отрицательной окружности.

И рисую картинку:

введите здесь описание изображения

Круг 1->2->3->1

Но программа об этом не сообщает.

Проанализируйте последние данные:

3 1 -3

//Следующий код проверяет наличие отрицательного круга. символ я был повторен до 5

if(dis[v[i]] > dis[v[i]] + w[i]){
    flag = 1;
}

//dis[1] now is -3 ,
//dis[3] now is 2 ,
//w[5] is -3

-3 > 2 + (-3) неверно!

Так вот в чем проблема,

Если я установлю вес 3->1 равным -100, алгоритм сможет обнаружить отрицательный круг.

1 2 2
0 1 -3
0 4 5
3 4 2
2 3 3
3 1 -100
Have circle

count = 4

Такова ли природа Беллман-форда?


person MMMMMCCLXXVII    schedule 24.05.2015    source источник
comment
Насколько отрицателен ваш цикл 1-2-3-1? его вес равен 2, что положительно...   -  person Petr    schedule 24.05.2015
comment
@Петр Мой! Я неправильно понимаю концепцию....   -  person MMMMMCCLXXVII    schedule 24.05.2015


Ответы (2)


Да, алгоритм Беллмана-Форда всегда может обнаружить отрицательные циклы. Если существует отрицательный цикл (например, когда вы устанавливаете 3->1 на -100), на графике нет кратчайшего пути, потому что вы всегда можете оставаться в цикле и получать больше отрицательных значений.

См., например. Википедия.

person dejvuth    schedule 24.05.2015

Да, Bellman-ford обнаружит отрицательный цикл со 100% точностью, без сомнения. Если ваша программа на C++, вы можете применить эту функцию:

#include <vector>
#include <set>
#include <utility>
#include <cstring>

using namespace std;

//Declare Global Variable for Data passing
#define MAX_NODE 1010 // 1 Million
#define INFINITY 1000000 // 1 Million

typedef pair<pair<int,int>,int> P; // 1-->2, Cost:3, then pair(node:1 node:2,cost:3)
vector<P> edgeList;
set<int> nodeList; // List of all nodes. Set prevents duplication.

int outDist[MAX_NODE];
int outPre[MAX_NODE];

bool BellmanFord(int source){

    // Initialize outDist
    for(int i=0;i<MAX_NODE;i++){
        outDist[i] = INFINITY;
    }
    //Initialize outPre
    memset(outPre,-1,sizeof(outPre));
    vector<P>::iterator edgeListItrEnd;
    vector<P>::iterator edgeListItrStart;
    P tmpPair;

    //we assume source distance is zero.
    outDist[source] = 0;

    int i,totalNode,rightNode,weight,leftNode;
    bool isComplete;
    totalNode = nodeList.size();

    edgeListItrEnd = edgeList.end();

    // Go through node 1 to N-1
    for(i=1; i<totalNode;i++){
        edgeListItrStart = edgeList.begin();
        // Iterate over all Edges
        isComplete = true;
        while(edgeListItrStart<edgeListItrEnd){
            tmpPair = *edgeListItrStart;
            leftNode = tmpPair.first.first;
            rightNode = tmpPair.first.second;
            weight = tmpPair.second;

            if((outDist[leftNode] != INFINITY) && (outDist[leftNode] + weight < outDist[rightNode])){
                //cout<<outDist[leftNode]<<" + "<<weight<<" < "<<outDist[rightNode]<<endl;
                isComplete = false;
                outDist[rightNode] = outDist[leftNode] + weight;
                outPre[rightNode] = leftNode;
            }
            edgeListItrStart++;
        }

        if(isComplete) return true;
    }

    // Check for Negative cycle;
    // Iterate over all edges
    edgeListItrStart = edgeList.begin();
    while(edgeListItrStart<edgeListItrEnd){
        tmpPair = *edgeListItrStart;
        leftNode = tmpPair.first.first;
        rightNode = tmpPair.first.second;
        weight = tmpPair.second;
        if((outDist[leftNode] != INFINITY) && (outDist[leftNode] + weight < outDist[rightNode])){
            return false;
        }
        edgeListItrStart++;
    }

    return true;
}

Вы можете увидеть варианты использования здесь: https://kt48.wordpress.com/2015/06/16/bellman-ford-algorithm-c-implementation/

person Community    schedule 16.06.2015