剖析bellman_ford算法
花费了1天半的时间终于把bellman_ford算法搞透彻,百度了许多大牛的博客但是其中有一点依然不明白,不过今天终于明白了,写一篇博客好好剖析剖析!!!
首先介绍一个概念:松弛:每次松弛操作实际上是对相邻节点的访问,第
次松弛操作保证了所有深度为n的路径最短。由于图的最短路径最长不会经过超过
条边,所以可知贝尔曼-福特算法所得为最短路径。
算法能解决的问题:判断最短路中是否有正环或负环。
算法具体流程:
1、循环n-1次。
2、对于每一次循环松弛边
3,最后判断一下松弛完的边是否还能松弛。
算法证明:
流程 1:
为什么要循环n-1次呢?
搜了好多博客,都没给讲明白,下面是我自己的理解,有错误之处欢迎大牛指教一二。
首先我们假设要有这样的常识,如果最短路存在,那么对于最短路中一定是无环的,如果有环的话那么我们可以一直沿着环走,那么我们得到的路一定不是最短路,所以对于n个点最多有n-1条边构成最短路,如果我们要得到的是最短路,那么我们就把除了原点以外的点初始化为无穷大(为了松弛),那么我们第一次松弛就得到了第一层的路,而除了原点以外的边并没有被松弛,想一想为什么?,如果是最长路那么就出使是的,我们第二次松弛的时候就把第一次松弛的点再往外扩
展一层,这是在他们的基础上在得到一层,说到这里大家就该明白为什么要循环n-1次。
流程 2:
d[v] = max(d[v],d[u]+w);
流程 3:
对于所有的点我们都已经松弛过,如果还能还能在松弛说明存在负权边。
poj1860 && poj 3259 是同样的题目没什么好说的。只介绍一下3259这道题。
n块地,m条边,w个虫洞
虫洞可以时间倒流,问你是否能回到从前。
思路:
对于虫洞加负权有向边,然后判断负环
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
int n,m,w,n1,m1,w1;
struct Node
{
int u,v,w;
}a[10000];
int cur = 0;
bool bellman()
{
int d[10000];
memset(d,63,sizeof(d));
d[1] = 0;
for(int i=1;i<n;i++)
{
bool finsh = false;
for(int j=0;j<cur;j++)
{
if(d[a[j].v]>d[a[j].u]+a[j].w){
d[a[j].v] = d[a[j].u] + a[j].w;
finsh = true;
}
}
if(!finsh)break;
}
for(int j=0;j<cur;j++)
{
if(d[a[j].v]>d[a[j].u]+a[j].w){
d[a[j].v] = d[a[j].u] + a[j].w;
return true;
}
}
return false;
}
int main()
{
int icase;
cin>>icase;
while(icase--)
{
cur = 0;
cin>>n>>m>>w;
while(m--)
{
cin>>n1>>m1>>w1;
a[cur].u = n1;
a[cur].v = m1;
a[cur++].w = w1;
a[cur].u = m1;
a[cur].v = n1;
a[cur++].w = w1;
}
while(w--)
{
cin>>n1>>m1>>w1;
a[cur].u = n1;
a[cur].v = m1;
a[cur++].w = -w1;
}
//cout<<INT<<endl;
if(bellman())puts("YES");
else
puts("NO");
}
}