• poj3259


    题目链接:http://poj.org/problem?id=3259

    题意:一个农场主,有n个农场,农场之间有m条双向路,每条路有花费时间权值,有w个虫洞以路的形式给出,权值为可以回到多久之前。

    思路:虫洞可以看成是一条负权路,问题就转化成求一个图中是否存在负权回路;

    1.bellman_ford算法

    求距离用模板就行了,怎么判断有没有负权回路?

    判断边集E中的每一条边的两个端点是否收敛。

    通俗点就是判断每条边两个顶点到源点的距离差是否就是边值。

    如果差值都是边值很明显无负权回路,反正则有。

    即有负权回路时,存在d[边的终点]>d[边的起点]+边的权值。why?

    因为有负权回路时,遍历最后一遍是,环的起点更新变的更小,而与它连边的点没有变。

    没懂?看下面例子就知道了。

    比例:有三条边:1-->2 权值为1, 2-->3权值为2,  3-->1权值为-4。构成负权回路,bellman_ford算法会遍历n-1次(这里为2次)全部的边

    假设起点为1,终点为3,d[]表示到点1的距离。

    第一遍: d[1]=0,d[2]=1, d[3]=3,  遍历3-->1时 d[1]=-1;

    第一遍: d[1]=-1, d[2]=0, d[3]=2,  遍历3-->1时 d[1]=-2;

    可以看出环的起点1的距离变小了,但后面的边没来得及更新变得更小,所以使得d[2]=0>d[1]+1=-1。

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn=6000;
    struct node{
        int next,from,to,w;
    }edge[maxn];
    
    int head[maxn],d[maxn],cnt,n,m,z;
    void init()
    {
        memset(head,-1,sizeof(head));
        memset(d,0x3f,sizeof(d));
        cnt=0;
    }
    
    void add(int u,int v,int w)//前向星连边 
    {
        edge[cnt].from=u;
        edge[cnt].to=v;
        edge[cnt].w=w;
        edge[cnt].next=head[u];
        head[u]=cnt++;
    }
    
    bool bellmanford()
    {
        d[1]=0;
        for(int i=0;i<n;i++)//板子 
        {
            for(int j=0;j<cnt;j++)
                if(d[edge[j].to]>d[edge[j].from]+edge[j].w)
                    d[edge[j].to]=d[edge[j].from]+edge[j].w;
        }
        for(int i=0;i<cnt;i++)//判断是否存在负权回路 
            if(d[edge[i].to]>d[edge[i].from]+edge[i].w)
                return true;
        return false;
    }
    
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            init();
            scanf("%d%d%d",&n,&m,&z);
            int a,b,c;
            for(int i=0;i<m;i++)
            {
                scanf("%d%d%d",&a,&b,&c);
                add(a,b,c);
                add(b,a,c);
            }
            for(int i=0;i<z;i++)//虫洞权值变负 
            {
                scanf("%d%d%d",&a,&b,&c);
                add(a,b,-c);
            }
            if(bellmanford())    printf("YES
    ");
            else    printf("NO
    ");
        }
        return 0;
    }
    View Code

    2.spfa算法

    那spfa怎么求负权回路呢?这个就比上面简单了。

    我们知道spfa如果碰到负权回路就会一直死循环,因为他会一直更新最短的路,有负权回路,环里的路权值会越来越小。

    如果没有负权回路,两点间有最短路的话,那么每个结点最多经过一次。也就是说,这条路不超过n-1条边。

    所以只要判断每个点入队的次数,次数>n-1就有负权回路了。

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<map>
    #include<vector>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn=6000;
    struct node{
        int to,next,w;
    }edge[maxn];
    int head[maxn],d[maxn],vis[maxn],num[maxn],cnt,n,m,z;
    
    void add(int u,int v,int w)
    {
        edge[cnt].to=v;
        edge[cnt].w=w;
        edge[cnt].next=head[u];
        head[u]=cnt++;
    }
    
    bool spfa()//spf板子 
    {
        memset(vis,0,sizeof(vis));
        memset(num,0,sizeof(num));
        queue<int> q;
        vis[1]=1;
        d[1]=0;
        num[1]++;
        q.push(1);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            vis[u]=0;
            for(int i=head[u];i!=-1;i=edge[i].next)
            {
                int v=edge[i].to;
                if(d[v]>d[u]+edge[i].w)
                {
                    d[v]=d[u]+edge[i].w;
                    if(!vis[v])
                    {
                        vis[v]=1;
                        q.push(v);
                        num[v]++;
                        if(num[v]>=n)//判断入队次数,是否构成负权回路。 
                            return true;
                    }
                }
            }
        }
        return false;
    }
    
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d%d",&n,&m,&z);
            cnt=0;
            memset(head,-1,sizeof(head));
            for(int i=1;i<=n;i++)
                d[i]=inf;
            int a,b,c;
            for(int i=0;i<m;i++)
            {
                scanf("%d%d%d",&a,&b,&c);
                add(a,b,c);
                add(b,a,c);
            }
            for(int i=0;i<z;i++)
            {
                scanf("%d%d%d",&a,&b,&c);
                add(a,b,-c);//虫洞权值变负 
            }
            if(spfa())    printf("YES
    ");
            else    printf("NO
    ");
        }
        return 0;
    }
    View Code
  • 相关阅读:
    linux下svn命令大全
    php常用函数
    在centos上设置计划任务
    sphinx使用心得
    sphinx2.8.8的配置文件
    Mac使用
    sftp
    uwp应用在debug模式下运行正常,编译为release版本的时候抛出异常
    win10 uwp 读取resw资源文件
    dll被设置为用记事本打开的解决方法
  • 原文地址:https://www.cnblogs.com/xiongtao/p/10065689.html
Copyright © 2020-2023  润新知