uva11090Going in Cycle!!(最短路spfa)

这道题使我彻底弄明白了spfa为什么最多是n-1个点入队,多一个点入队,就证明是有负环必然,举个栗子
这里写图片描述
比如说读边的顺序呢是这样的1——5,1——2,2——5,2——3,3——5,3——4,4——5,那么1号点先入队列,然后是5号,和2号入队列,1号出队列,5号点出队列,现在队列里只剩2号点,然后把5号和3号入队,5号出队列,然后把与3号相连的且此时不在队列的5,4入队列,3号出队列,然后5号出队列,然后再把与4号相连的5号入队列,这样,5号点进入了4次队列,而且在没有负环的图里最多只能进n-1次队列,即其他的n-1个点都与它相连,并且当时它并未在队列
下面让我们来探讨一下这个题首先这是一道找环的问题,那么我们可以,把所有边都减去一个值,如果出现了负环的情况,则证明,这是一个环,实际上我们减去的这个值,就是我们要求得平均值,这个题用了二分(我也是看了网上的题解才知道用二分的,没办法实在是太菜了),其实这道题用bellman-ford,和floyd找最小环应该都能做,因为只有50个点,另外既然用spfa做了,就需要考虑这个算法所需要考虑的情况,首先就是可能出现1与2相连,3与4相连,4与3相连,正常情况下,我们是找1为起点,然后开始找最短路即可,但是这道题目是找环,那么就会存在不是连通图的情况,实际上最短路也存在,但是不需要考虑,为什么呢,因为给了起点,找起点到其他地方的距离,到不了的根本不用考虑啊。对吧,所以这种情况下,就有两种选择,第一,刚开始把所有点都入队列,过倒是可以过,但是我并没找到初始化的方法,第二种,也就是我现在代码的这种,就是在减去一个值的同时,把所有点扫一遍,看是否能成环,如果可以,则继续进行
还有一点需要考虑的就是减掉的值在spfa找过之后,一定要再给他加回来,要不然原来的数据就被破坏了,无法进行下一组,最后,别忘了double

#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=100;
struct Edgenode
{
    int to,next;
    double w;
};
Edgenode edges[maxn*maxn];
int n,m;
int head[maxn],cnt[maxn],edge;
bool vis[maxn];

double dis[maxn];
void add(int u,int v,double c)
{
    edges[edge].w=c;
    edges[edge].to=v;
    edges[edge].next=head[u];
    head[u]=edge++;
}
void init()
{
    memset(head,-1,sizeof(head));
    edge=0;
}
bool spfa(int y)
{
    queue<int>que;
    int u;
    for(int i=0; i<=n; i++)
    {
        dis[i]=INF;
        cnt[i]=0;
        //vis[i]=0;

    }
    dis[y]=0;
    que.push(y);
    memset(vis,0,sizeof(vis));
    vis[y]=1;
    while(!que.empty())
    {
        u=que.front();
        que.pop();
        vis[u]=false;
        for(int i=head[u]; i!=-1; i=edges[i].next)
        {
            int v=edges[i].to;
            double w=edges[i].w;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    vis[v]=true;
                    que.push(v);
                    if(++cnt[v]>=n)
                        return 0;
                }
            }
        }
    }
    return 1;
}

bool check(double x)
{
    bool flag=0;
    for(int i=1; i<=n; i++)
        for(int k=head[i]; k!=-1; k=edges[k].next)
            edges[k].w-=x;
    for(int i=1; i<=n; i++)
    {
        if(!spfa(i))//这块是把所有点找一遍,看看能否找到环
            flag=1;
    }
    for(int i=1; i<=n; i++)
        for(int k=head[i]; k!=-1; k=edges[k].next)
        {
            edges[k].w+=x;
        }
    return flag;
}
int main()
{
    int t,case2=1;
    int a,b;
    double w,l=INF*1.0,r=0.0,mid=0.0;
    cin>>t;
    while(t--)
    {
        init();
        //cin>>n>>m;
        scanf("%d%d",&n,&m);
        for(int i=1; i<=m; i++)
        {
            //cin>>a>>b>>w;
            scanf("%d%d%lf",&a,&b,&w);
            add(a,b,w);
            l=min(l,w);
            r=max(r,w);
        }
        //cout<<m<<endl;
        printf("Case #%d: ",case2++);
        //if(m==1)
        if(!check(r+1))//还有这里就是如果我减掉最大值+1,还是不能构成负环,那么就说明无法成环
            printf("No cycle found.\n");
        else
        {

            double s=1e-8;
            while(r-l>s)
            {
                mid=(r+l)/2.0;
                if(check(mid))
                {
                    r=mid;
                }
                else l=mid;
            }
            printf("%.2lf\n",r);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值