zoukankan      html  css  js  c++  java
  • [kuangbin带你飞]专题四 最短路练习

    对于最短路,我主要使用的就是dijkstra,Floyd,SPFA这三个算法。先来介绍一下这三个算法。

    1.  dijkstra算法。它适用于边权为正的情况,它是单源最短路,就是从单个源点出发到所有的结点的最短路,它同时适用于无向图和有向图。

       它是基于贪心的思想,看下伪代码。

       1.清除所有点的标记

       2.设d[0]=0,其他的d[i]=INF;

       3.循环n次{

        在所有的未标记的结点中,选取d值最小的结点x,

        给结点x标记

        对于所有从x出发的所有的边(x,y),更新d[y]=min(d[y],d[x]+w[x][y]);

        }

       我们用例子来看下它的流程

       

       定义源点为0,dist[i]为源点0到顶点i的最短路径。其过程描述如下:

       第1步:从源点0开始,找到与其邻接的点:1,2,3,更新dist[]数组,因0不与4邻接,故dist[4]为正无穷。在dist[]中找到最小值,其顶点为2,即此时已找到0到2的最短路。

       第2步:从2开始,继续更新dist[]数组:2与1不邻接,不更新;2与3邻接,因0→2→3dist[3]大,故不更新dist[3] ;2与4邻接,因0→2→4dist[4]小,故更新dist[4]为4。

       在dist[]中找到最小值,其顶点为3,即此时又找到0到3的最短路。

       第3步:从3开始,继续更新dist[]数组:3与1邻接,因0→3→1dist[1]小,更新dist[1]为5;3与4邻接,因0→3→4dist[4]大,故不更新。在dist[]中找到最小值,其顶点为4,

       即此时又找到0到4的最短路。

       第4步:从4开始,继续更新dist[]数组:4与1不邻接,不更新。在dist[]中找到最小值,其顶点为1,即此时又找到0到1的最短路。

       第5步:所有点都已找到,停止。

       

    我们看一下例题:

    A - Til the Cows Come Home POJ - 2387 

    Bessie is out in the field and wants to get back to the barn to get as much sleep as possible before Farmer John wakes her for the morning milking. Bessie needs her beauty sleep, so she wants to get back as quickly as possible. 

    Farmer John's field has N (2 <= N <= 1000) landmarks in it, uniquely numbered 1..N. Landmark 1 is the barn; the apple tree grove in which Bessie stands all day is landmark N. Cows travel in the field using T (1 <= T <= 2000) bidirectional cow-trails of various lengths between the landmarks. Bessie is not confident of her navigation ability, so she always stays on a trail from its start to its end once she starts it. 

    Given the trails between the landmarks, determine the minimum distance Bessie must walk to get back to the barn. It is guaranteed that some such route exists.

    Input

    * Line 1: Two integers: T and N 

    * Lines 2..T+1: Each line describes a trail as three space-separated integers. The first two integers are the landmarks between which the trail travels. The third integer is the length of the trail, range 1..100.

    Output

    * Line 1: A single integer, the minimum distance that Bessie must travel to get from landmark N to landmark 1.

    Sample Input

    5 5
    1 2 20
    2 3 30
    3 4 20
    4 5 20
    1 5 100

    Sample Output

    90

    题意:有N个点,给出从a点到b点的距离,当然a和b是互相可以抵达的,问从1到n的最短距离。这就是最短路的模板题。
    #include <iostream>
    #include <stdio.h>
    #include <string>
    #include <string.h>
    #include <algorithm>
    #include <math.h>
    #include <queue>
    #include <vector>
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long LL;
    const int maxn=1005;
    int T,n;
    int u,v,val;
    struct Edge
    {
        int from,to,val;
        Edge(int f,int t,int v):from(f),to(t),val(v){}
    };
    struct Node
    {
        int id,w;
        Node(int idd,int ww):id(idd),w(ww) {}
        bool operator < (const Node& b) const
        {
            return w>b.w;
        }
    };
    bool vis[maxn];int dis[maxn];
    vector<int>G[maxn];vector<Edge>edge;
    
    void addedge(int f,int t,int val)
    {
        edge.push_back(Edge(f,t,val));
        int si=edge.size();
        G[f].push_back(si-1);
        edge.push_back(Edge(t,f,val));
        si=edge.size();
        G[t].push_back(si-1);
    }
    void dijkstra(int sta)
    {
        memset(vis,false,sizeof(vis));
        for(int i=0;i<=n;i++)
            dis[i]=INF;
        dis[sta]=0;
        priority_queue<Node>q;
        q.push(Node(sta,0));
        while(!q.empty())
        {
            Node temp=q.top();
            q.pop();
            int u=temp.id;
            if(vis[u])
            {
                if(u==n)
                    break;
                else
                    continue;
            }
            vis[u]=true;
            int len=G[u].size();
            for(int i=0;i<len;i++)
            {
                Edge e=edge[G[u][i]];
                if(dis[e.to]>dis[u]+e.val)
                {
                    dis[e.to]=dis[u]+e.val;
                    q.push(Node(e.to,dis[e.to]));
                }
            }
        }
    }
    int main()
    {
        scanf("%d %d",&T,&n);
        while(T--)
        {
            scanf("%d %d %d",&u,&v,&val);
            addedge(u,v,val);
        }
        dijkstra(1);
        printf("%d
    ",dis[n]);
        return 0;
    }

     

    2.  Floyd算法。如果需要求出每两点之间的最短路,不需要调用n次dijkstra算法,就可以用Floyd,但是要注意它是一个O(n^3)的算法,所以要注意使用的时候的情况,

       它的核心代码就是这块:

    for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    d[i][j]=min(d[i][j],d[i][k]+d[k][j]);

       它有几个注意的细节,首先是初始化,初始化是d[i][i]=0,其他的设为INF,但是这个INF不能定义的太大,防止d[i][k]+d[k][j]溢出的问题,但是INF不能太小,防止INF真的变成最短路中的一部分,所以一般估计一下实际的最短路的上限,INF设置的比这个上限高一些就可以了。

       而且有时候不关心路径的长度,只关心两点之间是否连通的时候,就是传递闭包问题,可以使用Floyd求,只要d[i][j]=min(d[i][j],d[i][k]+d[k][j]);换成d[i][j]=d[i][j] || (d[i][k]&&d[k][j]);就行意思就是i,j直接连通或者经过k两个连通,符合一种情况i,j就连通的。或者求1-n路径上经过的最大值最小,也是一样可以用Floyd(这种看情况),一般只有多次询问任意两点的最短路或者传递闭包的时候才用Floyd,因为它的时间复杂度太高了,经常爆时间,用的时候要注意。

    看下例题:

    B - Frogger POJ - 2253 

    Freddy Frog is sitting on a stone in the middle of a lake. Suddenly he notices Fiona Frog who is sitting on another stone. He plans to visit her, but since the water is dirty and full of tourists' sunscreen, he wants to avoid swimming and instead reach her by jumping. 
    Unfortunately Fiona's stone is out of his jump range. Therefore Freddy considers to use other stones as intermediate stops and reach her by a sequence of several small jumps. 
    To execute a given sequence of jumps, a frog's jump range obviously must be at least as long as the longest jump occuring in the sequence. 
    The frog distance (humans also call it minimax distance) between two stones therefore is defined as the minimum necessary jump range over all possible paths between the two stones. 

    You are given the coordinates of Freddy's stone, Fiona's stone and all other stones in the lake. Your job is to compute the frog distance between Freddy's and Fiona's stone. 

    Input

    The input will contain one or more test cases. The first line of each test case will contain the number of stones n (2<=n<=200). The next n lines each contain two integers xi,yi (0 <= xi,yi <= 1000) representing the coordinates of stone #i. Stone #1 is Freddy's stone, stone #2 is Fiona's stone, the other n-2 stones are unoccupied. There's a blank line following each test case. Input is terminated by a value of zero (0) for n.

    Output

    For each test case, print a line saying "Scenario #x" and a line saying "Frog Distance = y" where x is replaced by the test case number (they are numbered from 1) and y is replaced by the appropriate real number, printed to three decimals. Put a blank line after each test case, even after the last one.

    Sample Input

    2
    0 0
    3 4
    3
    17 4
    19 4
    18 5
    
    0
    
    Sample Output
    Scenario #1
    Frog Distance = 5.000
    
    Scenario #2
    Frog Distance = 1.414
    题意:就是刚说的最大值最小的问题。先预处理出点之间的距离,然后跑Floyd就可以了,因为这题n只有200,所以没有卡时间,能过,但是C就过不了了。
    #include <iostream>
    #include <stdio.h>
    #include <string>
    #include <string.h>
    #include <algorithm>
    #include <math.h>
    #include <queue>
    #include <vector>
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long LL;
    const int maxn=1005;
    int n;
    struct Point
    {
        int x,y;
    }point[maxn];
    double ma[maxn][maxn];
    void Floyd()
    {
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    ma[i][j]=min(ma[i][j],max(ma[i][k],ma[k][j]));
    }
    int main()
    {
        int casee=1;
        while(scanf("%d",&n)!=EOF)
        {
            if(n==0)
                break;
            for(int i=1;i<=n;i++)
                scanf("%d %d",&point[i].x,&point[i].y);
            for(int i=1;i<=n;i++)
            {
                for(int j=i+1;j<=n;j++)
                {
                    double cost=(point[i].x-point[j].x)*(point[i].x-point[j].x)+(point[i].y-point[j].y)*(point[i].y-point[j].y);
                    cost=sqrt(cost);
                    ma[i][j]=ma[j][i]=cost;
                }
            }
            Floyd();
            printf("Scenario #%d
    ",casee++);
            printf("Frog Distance = %.3f
    
    ",ma[1][2]);
        }
        return 0;
    }

     

    C - Heavy Transportation POJ - 1797 

    Background 
    Hugo Heavy is happy. After the breakdown of the Cargolifter project he can now expand business. But he needs a clever man who tells him whether there really is a way from the place his customer has build his giant steel crane to the place where it is needed on which all streets can carry the weight. 
    Fortunately he already has a plan of the city with all streets and bridges and all the allowed weights.Unfortunately he has no idea how to find the the maximum weight capacity in order to tell his customer how heavy the crane may become. But you surely know. 

    Problem 
    You are given the plan of the city, described by the streets (with weight limits) between the crossings, which are numbered from 1 to n. Your task is to find the maximum weight that can be transported from crossing 1 (Hugo's place) to crossing n (the customer's place). You may assume that there is at least one path. All streets can be travelled in both directions.

    Input

    The first line contains the number of scenarios (city plans). For each city the number n of street crossings (1 <= n <= 1000) and number m of streets are given on the first line. The following m lines contain triples of integers specifying start and end crossing of the street and the maximum allowed weight, which is positive and not larger than 1000000. There will be at most one street between each pair of crossings.

    Output

    The output for every scenario begins with a line containing "Scenario #i:", where i is the number of the scenario starting at 1. Then print a single line containing the maximum allowed weight that Hugo can transport to the customer. Terminate the output for the scenario with a blank line.

    Sample Input

    1
    3 3
    1 2 3
    1 3 4
    2 3 5
    

    Sample Output

    Scenario #1:
    4

    题意:和B一样,但是这题你要是用Floyd去做就会出现这个结果
       


       因为这题n达到1000,时间承受不了O(n^3)的时间复杂度。所以就要另辟道路了。

    #include <iostream>
    #include <stdio.h>
    #include <string>
    #include <string.h>
    #include <algorithm>
    #include <math.h>
    #include <queue>
    #include <vector>
    using namespace std;
    typedef long long LL;
    const int maxn=1005;
    const int INF=0x3f3f3f3f;
    int T,n,m;
    int a,b,v;
    struct Edge
    {
        int from,to,val;
        Edge(int f,int t,int v):from(f),to(t),val(v){}
    };
    struct Node
    {
        int id,w;
        Node(int idd,int ww):id(idd),w(ww) {}
        bool operator < (const Node& b) const
        {
            return w<b.w;
        }
    };
    bool vis[maxn];int dis[maxn];
    vector<int>G[maxn];vector<Edge>edge;
    void addedge(int f,int t,int val)
    {
        edge.push_back(Edge(f,t,val));
        int si=edge.size();
        G[f].push_back(si-1);
        edge.push_back(Edge(t,f,val));
        si=edge.size();
        G[t].push_back(si-1);
    }
    void dijkstra(int sta)
    {
        memset(vis,false,sizeof(vis));
        for(int i=0;i<=n;i++)
            dis[i]=0;
        dis[sta]=INF;
        priority_queue<Node>q;
        q.push(Node(sta,dis[sta]));
        while(!q.empty())
        {
            Node temp=q.top();
            q.pop();
            int u=temp.id;
            if(vis[u])
                continue;
            vis[u]=true;
            int len=G[u].size();
            for(int i=0;i<len;i++)
            {
                Edge e=edge[G[u][i]];
                if(dis[e.to]<min(dis[u],e.val))
                {
                    dis[e.to]=min(dis[u],e.val);
                    q.push(Node(e.to,dis[e.to]));
                }
            }
        }
    }
    int main()
    {
        int casee=1;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d %d",&n,&m);
            for(int i=0;i<=n;i++)
                G[i].clear();
            edge.clear();
            for(int i=0;i<m;i++)
            {
                scanf("%d %d %d",&a,&b,&v);
                addedge(a,b,v);
            }
            dijkstra(1);
            printf("Scenario #%d:
    ",casee++);
            printf("%d
    
    ",dis[n]);
        }
        return 0;
    }

    H - Cow Contest POJ - 3660 

    N (1 ≤ N ≤ 100) cows, conveniently numbered 1..N, are participating in a programming contest. As we all know, some cows code better than others. Each cow has a certain constant skill rating that is unique among the competitors.

    The contest is conducted in several head-to-head rounds, each between two cows. If cow A has a greater skill level than cow B (1 ≤ A ≤ N; 1 ≤ B ≤ NA ≠ B), then cow A will always beat cow B.

    Farmer John is trying to rank the cows by skill level. Given a list the results of M(1 ≤ M ≤ 4,500) two-cow rounds, determine the number of cows whose ranks can be precisely determined from the results. It is guaranteed that the results of the rounds will not be contradictory.

    Input

    * Line 1: Two space-separated integers: N and M
    * Lines 2..M+1: Each line contains two space-separated integers that describe the competitors and results (the first integer, A, is the winner) of a single round of competition: A and B

    Output

    * Line 1: A single integer representing the number of cows whose ranks can be determined
     

    Sample Input

    5 5
    4 3
    4 2
    3 2
    1 2
    2 5
    

    Sample Output

    2

    题意:有n头牛比赛,m种比赛结果,最后问你一共有多少头牛的排名被确定了,其中如果a战胜b,b战胜c,则也可以说a战胜c,即可以传递胜负。求能确定排名的牛的数目。

    如果一头牛被x头牛打败,打败y头牛,且x+y=n-1,则我们容易知道这头牛的排名就被确定了,所以我们只要将任何两头牛的胜负关系确定了,在遍历所有牛判断一下是否满足x+y=n-1,将满足这个条件的牛数目加起来就是所求解。

    抽象为简单的floyd传递闭包算法,在加上每个顶点的出度与入度 (出度+入度=顶点数-1,则能够确定其编号)。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    const int INF=0x3f3f3f3f;
    const int maxn=105;
    int n,m;
    int a,b;
    int ma[maxn][maxn];
    
    void Floyd()
    {
        for(int k=1;k<=n;k++)
        {
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                    ma[i][j]=ma[i][j]||(ma[i][k]&&ma[k][j]);
            }
        }
    }
    int main()
    {
        scanf("%d %d",&n,&m);
        memset(ma,0,sizeof(ma));
        while(m--)
        {
            scanf("%d %d",&a,&b);
            ma[a][b]=1;
        }
        Floyd();
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            int cnt=0;
            for(int j=1;j<=n;j++)
            {
                if(i==j)
                    continue;
                else if(ma[i][j]||ma[j][i])
                    cnt++;
            }
            if(cnt==n-1)
                ans++;
        }
        printf("%d
    ",ans);
        return 0;
    }

    I - Arbitrage POJ - 2240 

    Arbitrage is the use of discrepancies in currency exchange rates to transform one unit of a currency into more than one unit of the same currency. For example, suppose that 1 US Dollar buys 0.5 British pound, 1 British pound buys 10.0 French francs, and 1 French franc buys 0.21 US dollar. Then, by converting currencies, a clever trader can start with 1 US dollar and buy 0.5 * 10.0 * 0.21 = 1.05 US dollars, making a profit of 5 percent. 

    Your job is to write a program that takes a list of currency exchange rates as input and then determines whether arbitrage is possible or not. 

    Input

    The input will contain one or more test cases. Om the first line of each test case there is an integer n (1<=n<=30), representing the number of different currencies. The next n lines each contain the name of one currency. Within a name no spaces will appear. The next line contains one integer m, representing the length of the table to follow. The last m lines each contain the name ci of a source currency, a real number rij which represents the exchange rate from ci to cj and a name cj of the destination currency. Exchanges which do not appear in the table are impossible. 
    Test cases are separated from each other by a blank line. Input is terminated by a value of zero (0) for n.

    Output

    For each test case, print one line telling whether arbitrage is possible or not in the format "Case case: Yes" respectively "Case case: No".

    Sample Input

    3
    USDollar
    BritishPound
    FrenchFranc
    3
    USDollar 0.5 BritishPound
    BritishPound 10.0 FrenchFranc
    FrenchFranc 0.21 USDollar
    
    3
    USDollar
    BritishPound
    FrenchFranc
    6
    USDollar 0.5 BritishPound
    USDollar 4.9 FrenchFranc
    BritishPound 10.0 FrenchFranc
    BritishPound 1.99 USDollar
    FrenchFranc 0.09 BritishPound
    FrenchFranc 0.19 USDollar
    
    0
    

    Sample Output

    Case 1: Yes
    Case 2: No

    题意:给你m种货币,给你m种货币兑换规则,问通过这些规则最后能不能盈利。eg:1美元换0.5英镑,1英镑换10法郎,1法郎换0.21美元,这样1美元能换0.5*10*0.21=1.05美元,净赚0.05美元。

    思路:用Floyd找出每两种钱之间的最大兑换关系,遍历一遍,看有没有那种钱币最后能盈利,有就输出Yes,没有就是No。在处理钱币名称与编号之间的关系时,可以用map存,当然也可以用字符串比较。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <map>
    using namespace std;
    const int INF=0x3f3f3f3f;
    const int maxn=55;
    int n,m;
    double r;string s1,s2;
    double dis[maxn][maxn];
    map<string,int>ma;
    void Floyd()
    {
        for(int k=1;k<=n;k++)
        {
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    if(dis[i][j]<dis[i][k]*dis[k][j])
                        dis[i][j]=dis[i][k]*dis[k][j];
                }
            }
        }
    }
    int main()
    {
        int casee=1;
        while(scanf("%d",&n)!=EOF)
        {
            if(n==0)
                break;
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    if(i==j)
                        dis[i][j]=1;
                    else
                        dis[i][j]=0;
                }
            }
            for(int i=1;i<=n;i++)
            {
                cin>>s1;
                ma[s1]=i;
            }
            scanf("%d",&m);
            for(int i=0;i<m;i++)
            {
                cin>>s1>>r>>s2;
                dis[ma[s1]][ma[s2]]=r;
            }
            Floyd();
            bool flag=false;
            for(int i=1;i<=n;i++)
            {
                if(dis[i][i]>1)
                {
                    flag=true;
                    break;
                }
            }
            printf("Case %d: ",casee++);
            if(flag)
                printf("Yes
    ");
            else
                printf("No
    ");
        }
        return 0;
    }
  • 相关阅读:
    form表单有条件的提交
    当月第一天、最后一天、下月第一天,时间date
    网站分享
    如何做浏览器网站搜索
    js关闭当前页面跳转新页面
    img图片居中
    laravel 重定向路由带参数
    线段判严格相交+思维——poj1066
    线段判非严格相交+暴力——poj2653
    线段判严格相交+最短路建图——poj1556
  • 原文地址:https://www.cnblogs.com/jkzr/p/10003471.html
Copyright © 2011-2022 走看看