SPFA求负环

\( O(n*m)spfa求负环时间复杂度较高\\ 负环:图中存在一个环,环上个边的权值和为负数\\ 01分数规划\\ 求负环的方法,基于SPFA:\\ (1)统计某个点入队的次数,如果某个点入队n次,则说明存在负环\\ (2)统计当前每个点的最短路所含的边数,如果某点的最短路的边数>=n,则存在负环。\\ 图中的负环不一定从1点点走到,它可以从任何一个位置开始走,走到负环。\\ 处理方法:\\ 1.一开始让所有n个点入队,即假设有一个虚拟源点,源点到n个点的距离为0;\\ 2.为什么dis[N]不用初始化为0x3f3f3f3f,dis[N]可以任意初始化\\ 因为,如果存在一个负环<==>某些点到虚拟源点的距离是(—无穷),\\ 因为w[i]是有限值,spfa每在负环上转一圈,dis都会减一个有限值,\\ 但是,dis[负环]上的点会变成负无穷,所以spfa一定会在某个点满足判断负环条件的时候退出\\ 3.floyd和bellman-ford中0x3f3f3f3f/2的问题,如果1号点和其他点不连通,2->3,3会被2更新成0x3f3f3f3f-x\neq0x3f3f3f3f 当所有点入队次数超过2n时,我们认为图中很大可能存在负环。(取巧做法) \)

1165. 单词环

/*
每个字符串当成一个点,1e5个点,每个字符串相同
对于每个点,该点后可以连任何一个点,n-1
边数为n^2级别
将0~26^2每个十进制数看成一个点,每个字符串看成一条边
从aa-zz编号从0~675,一共676个点
1e5条边.
*/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

const int N=700,M=1e5+10;

int h[N],e[M],ne[M],idx,w[M];
int n,cnt[N];
double dis[N];
char str[M];
bool st[N];
void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
// queue<int>que;
int que[M*100],top;
bool inline check(double mid)
{
    memset(st,0,sizeof st);
    memset(cnt,0,sizeof cnt);
    //memset(que,0,sizeof que);重置top=0即可
    memset(dis,0,sizeof dis);
    top=0;
    for(int i=0;i<676;i++)
    {
        // que.push(i);
        que[++top]=i;
        st[i]=true;
    }
    int Count=0;
    while(top!=0)
    {
        // int t=que.front(),que.pop();
        int t=que[top--];
        st[t]=false;

        for(int i=h[t];~i;i=ne[i])
        {
            int j=e[i];
            if(dis[j]<dis[t]+w[i]-mid)
            {
                dis[j]=dis[t]+w[i]-mid;
                cnt[j]=cnt[t]+1;
                if(++Count>=10000)return true;
                if(cnt[j]>=N)
                    return true;
                if(!st[j])
                {
                    // que.push(j);
                    que[++top]=j;
                    st[j]=1;
                }
            }
        }
    }
    return false;
}

int main()
{
    while(scanf("%d",&n),n)
    {
        memset(h,-1,sizeof h);
        idx=0;
        for(int i=0;i<n;i++)
        {
            scanf("%s",str);
            int len=strlen(str);
            if(len>=2){
                int left=(str[0]-'a')*26+str[1]-'a';
                int right=(str[len-2]-'a')*26+str[len-1]-'a';
                add(left,right,len);
            }
        }
        if(!check(0))//将M==0代入,没有正环
            cout<<"No solution"<<endl;
        else
        {
            double l=0,r=1000;
            while(r-l>1e-4)
            {
                double mid=(l+r)/2;
                if(check(mid))
                    l=mid;
                else
                    r=mid;
            }
            // cout<<l<<endl;
            printf("%lf\n",r);
        }

    }
    return 0;
}
坑点,memest()的时间复杂度是O(n)的,在多组测试数据中,使用for初始化比memset要快
posted @ 2020-12-09 14:53  30天CF上蓝!!!  阅读(75)  评论(0编辑  收藏  举报