UVA 11090 Bellman-Ford,最短路[1]

https://vjudge.net/problem/UVA-11090

题意:给n条边和权值,求能形成的环中,权值和除以点的个数的值的最小值

思路:其实做的时候一点思路也没有,在网上看了别人的题解,还是很简单的,思维还是没锻炼到啊....,就是用二分判断mid,将所有的边权减去mid,如果这时候有负环的话,则说明这个mid是可以满足的,比如说没减去mid时,有一个k个点形成的环的权值和为k*mid-1;那么这个环是满足的,但是我们没有办法求这个k个点的环,所以我们将权值间去mid,则这个环的权值就是负的了,所以我们可以判断负环.....PS:真的是机智,还是太弱
--------------------- 

https://blog.csdn.net/xl2015190026/article/details/54983364

显然圈有很多,每个圈的长度也有很多。最简单的思路就是找到所有长度,或者所有最短长度,然后从中挑一个最小的就好了,但是好像没有什么算法能找到所有圈以及他们的所有长度或最短长度的。我最多知道如何判断一个图中有没有圈,而且这样的算法还不少。比如dfs,Floyd判圈法,Bellman-Ford等。。。

题目只是问你最短的长度是多少。所以也没有必要找出所有的圈。由于圈的长度是全局的性质,所以也很难通过dfs来计算长度。似乎和长度有关的圈的算法我就只会Bellman-Ford了。“判断有没有”的算法和二分往往是好搭档,这个组合经常出现在找最优解的问题上,当然前面也有不少和其他算法组合的题目,但是二分最为经典。本题也不例外。Bellman-Ford是关于负圈的,所以应该想到要在权值上做文章。题目求的是平均长度,所以不但和总长度有关,还和点的个数有关。而Bellman-Ford算法就是一个能总览全局的算法。不像dfs那样只能在局部思考。

下面的Bell满代码实际上是进行过优化的。实际上是SPFA的广度优先版本

简单介绍一下SPFA算法

首先呢,SPFA(Shortest Path Faster Algorithm)算法是求单源最短路径的一种算法,它有一个重要的功能是判负环(在差分约束系统中会得以体现)

我们先说一下松弛操作,它的原理是著名的定理:“三角形两边之和大于第三边”,在信息学中我们叫它三角不等式。若用 u 来松弛 v,就是判断是否 dis[ v ] > dis[ u ] + w[ u , v ],如果该式成立则将 dis[ v ] 更新到 dis[ u ] + w[ u , v ],否则不变

SPFA相当于是Bellman-Ford算法的一个优化版本,在Bellman-Ford算法中,很多松弛操作其实都是没有必要的,例如对于一条从 x 到 y 的边,如果连 x 都还没被松弛,那 y 肯定也还不能被 x 松弛,为了避免“用一个还没有被松弛的点去松弛另外的点”的情况,我们用一个队列来存储已经被松弛过的点,然后用队列里的点去松弛其他点,这就是SPFA算法的基本思想

判断给定的有向图中是否存在负环。

      利用 spfa 算法判断负环有两种方法:

      1) spfa 的 dfs 形式,判断条件是存在一点在一条路径上出现多次。

      2) spfa 的 bfs 形式,判断条件是存在一点入队次数大于总顶点数。

https://blog.csdn.net/ws_yzy/article/details/49657815

#include<bits/stdc++.h>
#include<limits.h>
using namespace std;
const int maxn =  55;
const int maxm = maxn*maxn;
const double eps =1e-3;
 
struct Edge
{
    int from,to;
    double dist;
};
 
struct Bellman_ford
{
    int n,m;
    vector<Edge>edges;
    vector<int>G[maxn];
    double d[maxn];
    int inq[maxn],cnt[maxn];
    void init(int n)
    {
        this->n=n;
        for(int i=0;i<n;i++) G[i].clear();
        edges.clear();
    }
    void add(int u,int v,double dist)
    {
        edges.push_back((Edge){u,v,dist});
        m=edges.size();
        G[u].push_back(m-1);
    }
    bool circle()
    {
        queue<int>Q;
        for(int i=0;i<n;i++)
        {
            d[i]=cnt[i]=0;
            inq[i]=1;
            Q.push(i);
        }
        while(!Q.empty())
        {
            int u=Q.front();Q.pop();
            inq[u]=0;
            for(unsigned int i=0;i<G[u].size();i++)
            {
                Edge& e=edges[G[u][i]];
                if(d[e.to]>d[e.from]+e.dist)
                {
                    d[e.to]=d[e.from]+e.dist;
                    if(!inq[e.to])
                    {
                        inq[e.to]=1;
                        Q.push(e.to);
                        if(++cnt[e.to]>n) return true;
                    }
                }
            }
        }
        return false;
    }
};
 
Bellman_ford BF;
 
int u[maxm],v[maxm];
double w[maxm];
 
int main()
{
    int T,n,m;
    scanf("%d",&T);
    for(int t=1;t<=T;t++)
    {
        scanf("%d %d",&n,&m);
        double MAX=0;
        for(int i=0;i<m;i++)
        {
            scanf("%d %d %lf",u+i,v+i,w+i);
            u[i]--;
            v[i]--;
            MAX+=w[i];
        }
        MAX=MAX*2;
        double l=0,r=MAX;
        while(r-l>eps)
        {
            double mid=l+(r-l)/2;
            BF.init(n);
            for(int i=0;i<m;i++)
                BF.add(u[i],v[i],w[i]-mid);
            if(BF.circle()) r=mid;
            else l=mid;
        }
        printf("Case #%d: ",t);
        if(l<eps||MAX-r<eps) puts("No cycle found.");
        else printf("%.2lf\n",l);
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值