zoukankan      html  css  js  c++  java
  • 最短路问题

     


     算法汇总:

    • Floyd 算法

    • Dijkstra 算法

    • Bellman-Ford 算法

    • SPFA算法


    Floyd算法

    应该是几个算法当中最简单的了,虽然时间复杂度有点高。

    f[k][i][j]表示从i经过若干个编号不超过k的节点到j的最短路长度。于是有:

                     f[k][i][j]=min(f[k-1][i][j],f[k-1][i][k]+f[k-1][k][j])

    k这一维可以被省略,对结果没有影响。

                     f[i][j]=min(f[i][j],f[i][k]+f[k][j])

    传递闭包

    通过传递性推导出尽量多的元素之间的关系。

    核心代码:

    for(int i=1;i<=n;i++) f[i][i]=1;
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                f[i][j]|=f[i][k]&f[k][j];
            } 
        }
    }

    Sorting It All Out

    描述

    An ascending sorted sequence of distinct values is one in which some form of a less-than operator is used to order the elements from smallest to largest. For example, the sorted sequence A, B, C, D implies that A < B, B < C and C < D. in this problem, we will give you a set of relations of the form A < B and ask you to determine whether a sorted order has been specified or not.

    输入Input consists of multiple problem instances. Each instance starts with a line containing two positive integers n and m. the first value indicated the number of objects to sort, where 2 <= n <= 26. The objects to be sorted will be the first n characters of the uppercase alphabet. The second value m indicates the number of relations of the form A < B which will be given in this problem instance. Next will be m lines, each containing one such relation consisting of three characters: an uppercase letter, the character "<" and a second uppercase letter. No letter will be outside the range of the first n letters of the alphabet. Values of n = m = 0 indicate end of input.输出For each problem instance, output consists of one line. This line should be one of the following three:

    Sorted sequence determined after xxx relations: yyy...y.
    Sorted sequence cannot be determined.
    Inconsistency found after xxx relations.

    where xxx is the number of relations processed at the time either a sorted sequence is determined or an inconsistency is found, whichever comes first, and yyy...y is the sorted, ascending sequence.

    样例输入

    4 6

    A<B

    A<C

    B<C

    C<D

    B<D

    A<B

    3 2

    A<B

    B<A

    26 1

    A<Z

    0 0

    样例输出

    Sorted sequence determined after 4 relations: ABCD.

    Inconsistency found after 2 relations.

    Sorted sequence cannot be determined.

    这是一道有向的传递闭包问题,对于每个i<j的式子,令f[i][j]=1,其余都=0。

    用Floyd进行传递闭包后,判断如果有f[i][j]=f[j][i]=1,则说明矛盾。输出Inconsistency found after xxx relations.

    判断每个点的出度和入度并保存下来,如果加起来不等于n-1,则说明不能确定每一对的大小关系。输出Sorted sequence cannot be determined.

    除此之外,可以用拓扑排序,把每个点的顺序保存下来。再输出。

    最后附上我没有AC的代码(我真的不知道哪里错了),康康就行了,不要相信它。

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,tot;
    bool f[30][30],vis[30];
    int in[30],out[30];
    char sh[30];
    
    bool floyd()
    {
        for(int k=1;k<=n;k++)
        {
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    if(f[i][k]&&f[k][j]) f[i][j]=1;
                } 
            }
        }
        for(int i=1;i<n;i++)
        {
            for(int j=i+1;j<=n;j++)
            {
                if(f[i][j]==1&&f[j][i]==1) return 0;
            }
        }
        return 1;
    }
    
    bool check()
    {
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(f[i][j]) in[j]++,out[i]++;
            }
        }
        for(int i=1;i<=n;i++)
        if(in[i]+out[i]!=n-1) return 0;
        else return 1;
    }
    
    void topsort()
    {
        stack<int> q;
        int i;
        memset(vis,0,sizeof(vis));
        tot=0;
        for(i=1;i<=n;i++)
        {
            if(in[i]==0)
            {
                q.push(i);
                break;
            }
        }
        while(!q.empty())
        {
            int cur=q.top();
            q.pop();
            vis[cur]=1;
            sh[tot++]=cur-1+'A';
            for(int i=1;i<=n;i++)
            {
                if(vis[i]==0&&f[cur][i]) in[i]--;
                if(vis[i]==0&&in[i]==0&&i!=cur) q.push(i);
            }
        }
    }
    
    int read()
    {
        int f=1,x=0;
        char c;
        c=getchar();
        while(c<'0'||c>'9')
        {
            if(c=='-') f=-1;
            c=getchar();
        }
        while(c>='0'&&c<='9')
        {
            x=x*10+c-'0';
            x=x%10000007;
            c=getchar();
        }
        return x*f;
    }
    
    int main()
    {
        while(1)
        {
            n=read();m=read();
            if(n==0&&m==0) break;
            memset(f,0,sizeof(f));
            int flag1=0,flag2=0;
            
            for(int i=1;i<=m;i++)
            {
                char a,b,c;
                cin>>a>>b>>c;
                f[a-'A'+1][c-'A'+1]=1;
                if(flag1||flag2) continue;
                else if(floyd()==0)
                {
                    flag1=i;
                    continue;
                }
                else if(check())
                {
                    topsort();
                    flag2=i;
                }
            }
            if(flag1) printf("Inconsistency found after %d relations.
    ",flag1);
            else if(flag2)     printf("Sorted sequence determined after %d relations: %s.
    ",flag2,sh);
            else printf("Sorted sequence cannot be determined.
    ");
        }
        return 0;
    } 

     


    Dijkstra 算法

    算法流程:

    1.初始化dis[1]=0,其余节点的dis值为正无穷大。

    2.找出一个没有被标记的、dis[x]最小的节点x,再标记。

    3.找节点x的所有出边y,dis[y]=min(dis[y],dis[x]+z)。

    4.重复上述步骤,直到所有点都被标记。

    注意Dijkstra算法不适用于负权边。

    贴上模板 时间复杂度O(N2)

    void dijkstra()
    {
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(cis));
        dis[1]=0;
        for(int i=1;i<n;i++)
        {
            int x=0;
            for(int j=1;j<=n;j++)
            if(vis[j]==0&&(x==0||dis[j]<dis[x])) x=j;
            vis[x]=1;
            for(int y=1;y<=n;y++)
            dis[y]=min(dis[y],dis[x]+a[x][y]);
        }
    }

    堆优化(优先队列)

    O(mlogn)

    priority_queue< pair<int,int> >q;
    void dijkstra()
    {
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        dis[1]=0;
        q.push(make_pair(0,1));
        while(q.size())
        {
            int x=q.top().second;q.pop();
            if(vis[x]) continue;
            vis[x]=1;
            for(int i=head[x];i;i=next[i])
            {
                int y=ver[i],z=edge[i];
                if(dis[y]>dis[x]+z)
                {
                    dis[y]=dis[x]+z;
                    q.push(make_pair(-d[y],y));
                }
            }
        }
    }

    Bellman-Ford 算法

    1.扫描所有边(x,y,z),若dis[y]>dis[x]+z,则用dis[x]+z更新dis[y]。

    2.重复上述步骤,直到没有更新操作发生。

    时间复杂度为O(nm)。

    SPFA 算法

    队列优化的Bellman-man算法

    1.建立一个队列,初始队列只有起点。

    2.取出队头结点x,扫描它的所有出边(x,y,z),若dis[y]>dis[x]+z,则用dis[x]+z更新dis[y]。如果y不在队列中,则把y入队。

    3.重复直到队列为空。

    时间复杂度 O(km)~O(nm)。

    queue<int> q;
    void spfa()
    {
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        dis[1]=0;vis[1]=1;
        q.push(1);
        while(q.size())
        {
            int x=q.front();q.pop();
            vis[x]=0;
            for(int i=head[x];i;i=next[i])
            {
                int y=ver[i],z=edge[i];
                if(dis[y]>dis[x]+z)
                {
                    dis[y]=dis[x]+z;
                    if(!vis[y]) q,push(y),vis[y]=1;
                }
            }
        }
    }

    在有负权边的情况下,Bellman-Ford和SPFA算法也能使用。

    最优贸易

    描述

    C国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为1条。
    C国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。
    商人阿龙来到C国旅游。当他得知“同一种商品在不同城市的价格可能会不同”这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚一点旅费。设C国 n 个城市的标号从 1~n,阿龙决定从1号城市出发,并最终在 n 号城市结束自己的旅行。在旅游的过程中,任何城市可以被重复经过多次,但不要求经过所有 n 个城市。
    阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品——水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。因为阿龙主要是来C国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。
    现在给出 n 个城市的水晶球价格,m 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。

    输入格式

       第一行包含 2 个正整数n 和m,中间用一个空格隔开,分别表示城市的数目和道路的
    数目。
       第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这n 个城
    市的商品价格。
       接下来 m 行,每行有3 个正整数,x,y,z,每两个整数之间用一个空格隔开。如果z=1,表示这条道路是城市x 到城市y 之间的单向道路;如果z=2,表示这条道路为城市x 和城市y 之间的双向道路。

    输出格式

    一个整数,表示答案。

    样例输入

    5 5

    4 3 5 6 1

    1 2 1

    1 4 1

    2 3 2

    3 5 1

    4 5 2

    样例输出

    5

    数据范围与约定

    输入数据保证 1 号城市可以到达n 号城市。
    对于 10%的数据,1≤n≤6。
    对于 30%的数据,1≤n≤100。
    对于 50%的数据,不存在一条旅游路线,可以从一个城市出发,再回到这个城市。
    对于 100%的数据,1≤n≤100000,1≤m≤500000,1≤x,y≤n,1≤z≤2,1≤各城市
    水晶球价格≤100。

    题目大概意思就是说在带权值的图上找到一条从1到n的路径,使有两个点的权值差最大。

    先以1为起点,在原图上用SPFA,保存下一个数组d,表示从1到x的所有路径中,能够经过的权值最小的节点的权值。

    在求单源最短路径时,用min(d[x],price[y])更新d[y];

    再以n为起点,反图上用SPFA,保存一个f[x],与d类似。

    最后枚举每个节点x,用f[x]-d[x]更新答案。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100002;
    vector<int> q1[N],q2[N];
    int w[N],a[N],b[N];
    queue<int> q;    
    int n,m;
    inline int read()
    {
        int ans=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)) f*=(ch=='-')? -1:1,ch=getchar();
        do ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar();
        while(isdigit(ch));
        return ans*f;
    }
    void spfa1()
    {
        q.push(1);
        memset(a,127,sizeof(a));
        while(!q.empty())
        {
            int t=q.front();
            q.pop();
            a[t]=min(a[t],w[t]);
            int l=q1[t].size();
            for(int i=0;i<l;i++)
            {
                if(a[t]<a[q1[t][i]])
                {
                    a[q1[t][i]]=a[t];
                    q.push(q1[t][i]);
                }
            }
        }
    }
    void spfa2()
    {
        q.push(n);
        while(!q.empty())
        {
            int t=q.front();
            q.pop();
            b[t]=max(b[t],w[t]);
            int l=q2[t].size();
            for(int i=0;i<l;i++)
            {
                if(b[t]>b[q2[t][i]])
                {
                    b[q2[t][i]]=b[t];
                    q.push(q2[t][i]);
                }
            }
        }
    }
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++)
        {
            w[i]=read();
        }
        for(int i=1;i<=m;i++)
        {
            int x,y,z;
            x=read();y=read();z=read();
            q1[x].push_back(y);
            q2[y].push_back(x);
            if(z==2)
            {
                q1[y].push_back(x);
                q2[x].push_back(y);
            }
        }
        spfa1();
        spfa2();
        int ans=0;
        for(int i=0;i<=n;i++)
        ans=max(ans,b[i]-a[i]);
        printf("%d",ans);
        return 0;
    }

    我昨晚忘记发了QAQ……真的

    轻罚。

     

  • 相关阅读:
    SGU 106 The equation 扩展欧几里德
    poj 2478 Farey Sequence 欧拉函数前缀和
    uva 11752 The Super Powers 素数+大数判断大小
    Lightoj 1370 素数打表 +二分
    vijos 1250 最勇敢的机器人 分组背包+并查集
    Codeforces Round #366 (Div. 2) A , B , C 模拟 , 思路 ,queue
    BZOJ 4034: [HAOI2015]T2 树链剖分
    BZOJ 1057: [ZJOI2007]棋盘制作 悬线法求最大子矩阵+dp
    BZOJ 1260: [CQOI2007]涂色paint 区间DP
    BZOJ 1264: [AHOI2006]基因匹配Match 树状数组+DP
  • 原文地址:https://www.cnblogs.com/mgtnb/p/12214565.html
Copyright © 2011-2022 走看看