题目要求有向图G(V,E)是否存在负环,求负环的方法有Bellman-Ford以及SPFA。
Bellman-Ford:复杂度O(V*E),加入优化可0ms。
按照算法导论中的讲解,简单地理解就是若不存在负环,则源节点到各个节点的最短路径必定存在。假设v0,vk分别为始终点,路径为v0,v1,...,vk,最短路径最多只能有|V|-1条边组成。我们每次对全部边作松弛操作,最多进行|V|-1次上述操作,则我们进行的松弛操作的顺序必定包含有(v0,v1),(v1,v2),...,(vk-1,vk),根据归纳法可知经过上述顺序的松弛必定能找到v0到vk的最短路径。
若图中存在负环,显然从源点到环中的点的距离是可以一直减小至无穷小的,则我们可以一直松弛下去,即再进行第|V|次对全部边的松弛操作,会有点的距离减小则存在负环。
非连通图的负环判别:算法导论中只说Bellman-Ford能找出从源点出发的负环,但对于此题,负环可能不跟1节点连通,则我们不是要进行V*V*E的操作?当然是不必要的。
方法1:注意到算法导论中源点到其它点的距离初始化为无限大,所以无限大减去一个数仍然是无限大,所以与源点非连通的点永远不能被松弛。但是我们不能赋初值为无限大(直接判别除外),只能赋一个整型的大值,而且也不需要赋为无限大。只要存在负环,若产生松弛,则肯定能松弛下去,则我们赋各个距离初值为一个较大的值,当然这个值不能影响求最短距离,但是又要使跟源点非连通的点能产生松弛,注意到第一次这种松弛肯定产生于负权边上。
方法2:新生成一个节点,使它到所有节点距离为0,则从负权边出发可以对全部图进行松弛。也可以理解为以各个负权边起始点作为源点对图中个点进行松弛。
Bellman-Ford优化1:主要针对不存在负环的图,每当对全部边作松弛之前设置一个变量IsRelax为FALSE,代表没有松弛操作发生,若发生松弛则置其为true。当我们还未进行|V|-1次循环,但是已经没有点的距离减小的话,则我们再循环也不会有松弛发生,也不存在负环。
优化2:利用上述不连通图中方法2,并用并查集检查环路,注意到祖先肯定是负权边起始点,则起始点距离(初始化0)经过环路变小说明存在负环。
SPFA:类似于BFS,是Bellman-Ford的优化。当有点松弛时,且此点未在队列中,则我们把此点放入队列中再去松弛其他点,设pos[i]为i点在路径中位置,则经过i点松弛到的点的位置是pos[i]+1,当pos[x] == |V|时,说明存在负环。类似Bellman-Ford判无连通图的负环可以先把所有点加入队列中,并且加入并查集优化。
SPFA查负环性能比Bellman-Ford略差。
Bellman-Ford:
#include<cstdio>
#include<cstring>
#define maxN 502
#define maxM 5202
#define INF 0X7F7F
int fieldNum,edgeNum;
short g[maxN][maxN];
struct edge
{
short px,py;
}edges[maxM];
int Bellman_Ford(int nodeNum)
{
int i,j;
short x,y;
int dis[maxN];
short fa[maxN];
char IsRelax;
for(i = 1;i <= nodeNum;i++)
fa[i] = i;
memset(dis,0,sizeof(dis));
for(i = 1;i < nodeNum;i++)
{
IsRelax = false;
for(j = 0;j < edgeNum;j++)
{
x = edges[j].px;
y = edges[j].py;
if(dis[y] > dis[x]+g[x][y])
{
if(fa[x] == y) //有环且初始点距离被更新则说明存在负环
return true;
IsRelax = true;
fa[y] = fa[x];
dis[y] = dis[x]+g[x][y];
}
}
if(!IsRelax) //已经没有点被松弛,则再循环也不会松弛且说明没有负环
return false;
}
for(i = 0;i < edgeNum;i++)
{
x = edges[i].px;
y = edges[i].py;
if(dis[y] > dis[x]+g[x][y]) //负环能无限松弛
return true;
}
return false;
}
int generateGraph(char* s,bool edgeFlag)
{
int a[2];
int i = 0;
int num = 0;
while(*s)
{
if(*s != ' ')
{
num *= 10;
num += *s - '0';
}
else
{
a[i++] = num;
num = 0;
}
s++;
}
if(edgeFlag)
{
if(g[a[0]][a[1]] > num)
g[a[0]][a[1]] = g[a[1]][a[0]] = num;
}
else if(g[a[0]][a[1]] > -1*num)
g[a[0]][a[1]]= -1*num;
return 0;
}
int generateEdge()
{
int i,j;
edgeNum = 0;
for(i = 1;i <= fieldNum;i++)
for(j = 1;j <= fieldNum;j++)
{
if(g[i][j] != INF)
{
edges[edgeNum].px = i;
edges[edgeNum++].py = j;
}
}
return 0;
}
int main()
{
int i;
int farmNum,pathNum,holeNum;
char strIn[15];
scanf("%d",&farmNum);
while(farmNum--)
{
scanf("%d%d%d",&fieldNum,&pathNum,&holeNum);
memset(g,0X7F,sizeof(g));
getchar();
for(i = 0;i < pathNum+holeNum;i++)
{
gets(strIn);
if(i < pathNum)
generateGraph(strIn,1); //生成有向图
else
generateGraph(strIn,0);
}
generateEdge(); //提取出图中的边
if(Bellman_Ford(fieldNum))
printf("YES\n");
else printf("NO\n");
}
return 0;
}
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define maxN 502
#define INF 0X7F7F
int SPFA(int nodeNum,short (*g)[maxN])
{
short i;
short now;
char IsInQ[maxN];
short fa[maxN];
short updateNum[maxN];
int dis[maxN];
for(i = 1;i <= nodeNum;i++)
fa[i] = i;
memset(dis,0,sizeof(dis));
memset(updateNum,0,sizeof(updateNum));
queue<short> q;
for(i = 1;i <= nodeNum;i++)
{
q.push(i);
IsInQ[i] = true;
}
while(!q.empty())
{
now = q.front();
q.pop();
IsInQ[now] = false;
for(i = 1;i <= nodeNum;i++)
{
if(g[now][i] != INF&&dis[now]+g[now][i] < dis[i])
{
if(fa[now] == i)
return true;
fa[i] = fa[now];
dis[i] = dis[now]+g[now][i];
if(!IsInQ[i])
{
updateNum[i] = updateNum[now]+1;
if(updateNum[i] == nodeNum)
return true;
IsInQ[i] = true;
q.push(i);
}
}
}
}
return false;
}
int generateEdge(char* s,short (*g)[maxN],bool edgeFlag)
{
int a[2];
int i = 0;
short num = 0;
while(*s)
{
if(*s != ' ')
{
num *= 10;
num += *s - '0';
}
else
{
a[i++] = num;
num = 0;
}
s++;
}
if(edgeFlag)
{
if(g[a[0]][a[1]] > num)
g[a[0]][a[1]] = g[a[1]][a[0]] = num;
}
else if(g[a[0]][a[1]] > -1*num)
g[a[0]][a[1]]= -1*num;
return 0;
}
int main()
{
int i;
short g[maxN][maxN];
int farmNum,fieldNum,pathNum,holeNum;
char strIn[15];
scanf("%d",&farmNum);
while(farmNum--)
{
memset(g,0X7F,sizeof(g));
scanf("%d%d%d",&fieldNum,&pathNum,&holeNum);
getchar();
for(i = 0;i < pathNum+holeNum;i++)
{
gets(strIn);
if(i < pathNum)
generateEdge(strIn,g,1);
else
generateEdge(strIn,g,0);
}
if(SPFA(fieldNum,g))
printf("YES\n");
else printf("NO\n");
}
return 0;
}