uva11090Going in Cycle!!(最短路spfa)

来源:互联网 发布:mac怎么升级系统版本 编辑:程序博客网 时间:2024/06/05 00:28

这道题使我彻底弄明白了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
原创粉丝点击