“spfa判断正负环“以及“对偶操作建图“的应用

 算法流程:

 关于spfa判断正负环:

        注:N为点的数量,M为边的数量

        Bellman_Ford在求解最短路时,如果A->B,那么只有dist[A]被更新了以后,dist[B]才有必要被更新,但Bellman_Ford无论如何,都对每条边进行N- 1次松弛操作。

        Spfa针对这一点进行优化,如果松弛操作使某个点改变,且这个点不在队列中,就加进队列中。

        总共有n个点,因为如果cnt[j] >= n有大于等于n条边时,则证明有n + 1个点,根据抽屉原理说明这条路径有一个点被经过了两次,则成环

        可以用这个方法在求最短路的过程中判断负环
        因为是在dist数组更新时cnt[]才更新所以说明这个一定是负权环才会导致dist[]变小

        求正环同理,转换成最长路即可

SPFA(Shortest Path Faster Algorithm)是一种基于贪心思想的最短路径算法,它可以用于求解带有负权边的图中的单源最短路径问题。在一般的图中,如果存在负权边,那么可能存在负环。负环指的是图中一个环路,使得环路上的所有边的权值之和为负数。如果存在负环,那么最短路径可能不存在,因为可以一直绕着负环不断地降低路径长度,最终得到一个负无穷的路径长度。

SPFA算法可以判断图中是否存在负环,其主要思路是通过对每个点进行多次松弛操作,判断是否存在环路可以不断降低路径长度。具体实现方法是利用一个队列来维护需要进行松弛操作的点,每次取出队头的点,然后更新所有与之相邻的点的距离,如果某个点的距离发生了变化,就把它加入队列中等待后续的处理。如果某个点进队的次数超过了n次(n为图中点的数量),那么就说明存在负环。这是因为在一个无负环图中,任意两点之间的最短路径最多经过n-1条边,如果存在负环,那么可以不断绕着负环降低路径长度,因此在经过n次松弛操作之后,仍然存在点的距离发生了变化,这就意味着存在负环。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int M = 1e5 + 10,N = 26 * 26 + 50;
int h[N],e[M],w[M],ne[M],idx;
int n,cnt[N];
double dist[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx ++;
}

bool check(double mid)
{
    
    queue<int> q;
    memset(st, 0, sizeof st);
    memset(cnt, 0, sizeof cnt);
    int count = 0;
    
    for(int i = 0; i < 26 * 26; i ++ )
    {
        q.push(i);
        st[i] = true;
    }
        
    while(q.size())
    {
        int t = q.front();
        q.pop();
        st[t] = false;
        
        for(int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if(dist[j] < dist[t] + w[i] - mid)
            {
                dist[j] = dist[t] + w[i] - mid;
                cnt[j] = cnt[t] + 1;
                if(cnt[j] >= N) return true;
                if(++count > 10000 )return true;
                if(!st[j])
                {
                    st[j] = true;
                    q.push(j);
                }
            }
        }
        
        
        
        
    }
    
    
    return false;
    
}

int main()
{
    while(scanf("%d", &n), n)
    {
        char c[1010];
        memset(h, -1, sizeof h);
        idx = 0;
        for(int i = 1;i <= n; i ++ )
        {
            
            scanf("%s",c);
            int len = strlen(c);
         
            if(len >= 2)
            {
                int left = (c[0] - 'a') * 26 + c[1] - 'a';
                int right = (c[len - 2] - 'a') * 26 + c[len - 1] - 'a';
                add(left, right, len);
            }
        }
        
        if(!check(0))puts("No solution");
        
        else
        {
            double l = 0,r = 1000;
            while( r - l > 1e-4 )
            {
                double mid = (l + r) / 2;
                if(check(mid)) l = mid;
                else r = mid;
            }
           printf("%lf\n",r);
        }
        
    }
    
    
    
    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_66252872/article/details/129478422