对存在负权值的回路的首尾节点连接问题

bellman_ford队列优化

优化核心:通过引入一个队列实现选择性松弛边连接;
队列中进入元素:一个节点在离开的时候与其相连接的节点进入队列。

不理解的点

1.Bellman_ford算法中,为什么说对所有边松弛一次(遍历一次)得到的是与起点有一条连接的节点的最小值?连接两次是与起点有两条连接的起点的最小值?
2.Bellman_ford算法中,对所有边第一次遍历的过程中,一些还没有minDi[]节点的边连接实际是没有遍历的,只需要遍历这个没有遍历过的边就可以得到最短连接了。

bellman_ford之判断负权回路

题目理解

如果图中出现了负权回路,就会导致如果遍历边的次数发生变化,最终的权值就会被影响。
所以思路的逻辑是:多遍历一次。

Bellman_ford算法实现

#include<iostream>
#include<vector>
#include<list>
#include<climits>
using namespace std;
int main(){
    int n,m,p1,p2,val;
    cin>>n>>m;
    vector<vector<int>> grid;
    while(m--){
        cin>>p1>>p2>>val;
        grid.push_back({p1,p2,val});
    }
    int start=1;
    int end=n;
    vector<int> minDi(n+1,INT_MAX);
    minDi[start]=0;
    bool flag=false;
    for(int i=1;i<=n;i++){
        for(vector<int>&side:grid){
            int from=side[0];
            int to=side[1];
            int price=side[2];
        if(i<n){
            if(minDi[from]!=INT_MAX&&minDi[to]>minDi[from]+price){
                minDi[to]=minDi[from]+price;
            }
        }
        else{
            if(minDi[from]!=INT_MAX&&minDi[to]>minDi[from]+price){
                flag=true;
            }
        }
        }
    }  
    if(flag==true)  cout<<"circle"<<endl;
    else if(minDi[end]==INT_MAX) cout<<"unconnected"<<endl;
    else cout<<minDi[end]<<endl;
}

队列优化版Bellman_ford算法实现

?思路:有一个连接,就会入队一次。所有结点的入度不可能超过n-1,所以当某节点进入队列的次数超过n,就说明存在负权回路。

#include<iostream>
#include<vector>
#include<queue>
#include<list>
#include<climits>
using namespace std;
struct Edge{
    int to;
    int val;
    Edge(int t,int w):to(t),val(w){}
};
int main(){
    int n,m,p1,p2,val;
    cin>>n>>m;
    vector<list<Edge>> grid(n+1);
    vector<bool> isInQueue(n+1);
    for(int i=0;i<m;i++){
        cin>>p1>>p2>>val;
        grid[p1].push_back(Edge(p2,val));
    }
    int start=1;
    int end=n;
    vector<int> minDi(n+1,INT_MAX);
    vector<int> count(n+1,0);
    minDi[start]=0;
    queue<int> que;
    que.push(start);
    bool flag=false;
    while(!que.empty()){
        int node=que.front();
        que.pop();
        isInQueue[node]=false;
        for(Edge edge:grid[node]){
            int from=node;
            int to=edge.to;
            int value=edge.val;
            if(minDi[to]>minDi[from]+value){
                minDi[to]=minDi[from]+value;
                if(isInQueue[to]==false){
                    que.push(to);
                    isInQueue[to]=true;
                    count[to]++;
                    if(count[to]>=n){
                        flag=true;
                        while (!que.empty()) que.pop();
                    break;
                    }
                }
            }
        }
    }
    if(flag)cout<<"circle"<<endl;
    else if(minDi[end]==INT_MAX) cout<<"unexpected"<<endl;
    else cout<<minDi[end]<<endl;
}

最多经过K个城市的起点和终点的最小权值连接

疑问:

1.输入中是从起点到终点可以通过的K个节点,这个起点未必从1开始。
2.进行一次松弛操作,对所有边的遍历一次是否包括边的顺序,即如果边在前面,但是此时的minDi还没有数值,那么对该边的遍历是从第二次松弛开始算吗?对,在minDi[]违背更新的情况下,一次遍历是跳过了该节点的。

posted on 2026-01-12 18:56  FAfa_C++  阅读(7)  评论(0)    收藏  举报