对存在负权值的回路的首尾节点连接问题
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[]违背更新的情况下,一次遍历是跳过了该节点的。
浙公网安备 33010602011771号