1. 程式人生 > >最短路(Floyed、Dijkstra、Bellman-Ford、SPFA)

最短路(Floyed、Dijkstra、Bellman-Ford、SPFA)

media 入隊 name img ack nat 鄰接表 整數 red

一、Floyed-Warshall算法

枚舉中間點起點終點,對整個圖進行松弛操作,就能得到整個圖的多源最短路徑;

技術分享圖片

技術分享圖片

技術分享圖片

例:POJ2240  Arbitrage

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

Source

Ulm Local 1996 技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
const int maxn=35;
int n,m;
map<string,int> N;
double dis[maxn][maxn];

int main()
{
    int kase=1;
    //freopen("Atext.in","r",stdin);
    while(cin >> n,n)
    {
        string a,b;
        double tmp;
        bool flag=false;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                dis[i][j]=-1;
        for(int i=0;i<n;i++){
            cin >> a;
            N.insert(make_pair(a,i));
        }
        cin >> m;
        for(int i=0;i<m;i++)
        {
            cin >> a >> tmp >> b;
            dis[N[a]][N[b]]=tmp;        //鄰接矩陣存邊的信息;
        }
        for(int k=0;k<n;k++)
            for(int i=0;i<n;i++)
                for(int j=0;j<n;j++)
                if(i!=j&&j!=k&&k!=i&&dis[i][k]!=-1&&dis[k][j]!=-1)
                dis[i][j]=max(dis[i][k]*dis[k][j],dis[i][j]);
        /*for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
                cout << dis[i][j] << " ";
            cout << endl;
        }
        cout << endl;*/
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            if(dis[i][j]*dis[j][i]>1)flag=1;
        printf("Case %d: %s\n",kase,flag?"Yes":"No");
        N.clear();
        kase++;
    }
    return 0;
}

附:Warshall算法的傳遞閉包

技術分享圖片

技術分享圖片

技術分享圖片

例:POJ2253  Frogger

Description

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

Source

Ulm Local 1997 技術分享圖片

技術分享圖片

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=205;
struct node{
    int x,y;
}N[maxn];
int con[maxn][maxn],n;
double G[maxn][maxn];
void floyed()       //枚舉所有邊進行松弛;
{
    for(int k=0;k<n;k++)
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)    //松弛為最大邊的最小值;
            G[i][j]=min(G[i][j],max(G[i][k],G[k][j]));
}
int main()
{
    int kase=0;
    while(~scanf("%d",&n),n)
    {
        for(int i=0;i<n;i++)
            scanf("%d%d",&N[i].x,&N[i].y);
        for(int i=0;i<n;i++)        //這樣對稱的鄰接矩陣只須計算一半
            for(int j=i+1;j<n;j++)
                G[i][j]=G[j][i]=(double)sqrt(double(N[i].x-N[j].x)*(N[i].x-N[j].x)+double(N[i].y-N[j].y)*(N[i].y-N[j].y));
        floyed();
        printf("Scenario #%d\n",++kase);
        printf("Frog Distance = %.3lf\n\n",G[0][1]);
    }
    return 0;
}

二、Dijkstra算法

技術分享圖片

技術分享圖片

技術分享圖片

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=1005;
const int inf=0x3f3f3f3f;
int n;
struct node{
    int x,y;
}N[maxn];
bool vis[maxn];
double G[maxn][maxn],d[maxn];       //鄰接矩陣
void Dijkstra()
{
    fill(vis,vis+n,false);
    fill(d,d+n,inf);
    d[0]=0;
    while(true)
    {
        int v=-1;
        for(int i=0;i<n;i++)
            if(!vis[i]&&(v==-1||d[i]<d[v]))     //從尚未使用過的頂點中選取一個最小值;
                v=i;
        if(v==-1)
            break;
        vis[v]=true;
        for(int i=0;i<n;i++)
            d[i]=min(d[i],max(d[v],G[v][i]));   //源點到各點的最長邊的最小值;
    }
}
int main()
{
    int kase=0;
    while(~scanf("%d",&n),n)
    {
        for(int i=0;i<n;i++)
            scanf("%d%d",&N[i].x,&N[i].y);
        for(int i=0;i<n;i++)        //這樣對稱的鄰接矩陣只須計算一半
            for(int j=i+1;j<n;j++)
                G[i][j]=G[j][i]=(double)sqrt(double(N[i].x-N[j].x)*(N[i].x-N[j].x)+double(N[i].y-N[j].y)*(N[i].y-N[j].y));
        Dijkstra();
        printf("Scenario #%d\n",++kase);
        printf("Frog Distance = %.3f\n\n",d[1]);
    }
    return 0;
}

三、Bellman-Ford算法

四、SPFA算法(Shortest Path Faster Algorithm)

隊列優化的bellman-ford;

例:POJ1874  暢通工程續

Problem Description 某省自從實行了很多年的暢通工程計劃後,終於修建了很多路。不過路多了也不好,每次要從一個城鎮到另一個城鎮時,都有許多種道路方案可以選擇,而某些方案要比另一些方案行走的距離要短很多。這讓行人很困擾。

現在,已知起點和終點,請你計算出要從起點到終點,最短需要行走多少距離。

Input 本題目包含多組數據,請處理到文件結束。
每組數據第一行包含兩個正整數N和M(0<N<200,0<M<1000),分別代表現有城鎮的數目和已修建的道路的數目。城鎮分別以0~N-1編號。
接下來是M行道路信息。每一行有三個整數A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城鎮A和城鎮B之間有一條長度為X的雙向道路。
再接下一行有兩個整數S,T(0<=S,T<N),分別代表起點和終點。 Output 對於每組數據,請在一行裏輸出最短需要行走的距離。如果不存在從S到T的路線,就輸出-1.

Sample Input 3 3 0 1 1 0 2 3 1 2 1 0 2 3 1 0 1 1 1 2 Sample Output 2 -1 Author linle Source 2008浙大研究生復試熱身賽(2)——全真模擬 解法1:spfa  2.floyed  3.dijkstra
#include <iostream>
#include <queue>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn=205;
const int inf=0x3f3f3f3f;
typedef pair<int,int> p;
vector<p>E[maxn];//鄰接表;
int n,m,d[maxn],inq[maxn];      //d[maxn]到源點的最短距離,inq[maxn]在隊列裏的元素;
void init()
{
    for(int i=0;i<n;i++)E[i].clear();
    for(int i=0;i<n;i++)inq[i]=0;
    for(int i=0;i<n;i++)d[i]=inf;
}
void spfa(int x)
{
    queue<int> que;
    que.push(x),d[x]=0,inq[x]=1;
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        inq[now]=0;
        for(int i=0;i<E[now].size();i++)
        {
            int v=E[now][i].first;
            if(d[v]>d[now]+E[now][i].second)
            {
                d[v]=d[now]+E[now][i].second;
                if(inq[v]==0){
                    inq[v]=1;
                    que.push(v);
                }
            }
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        int a,b,c;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            E[a].push_back(p(b,c));
            E[b].push_back(p(a,c));
        }
        scanf("%d%d",&a,&b);
        spfa(a);
        if(d[b]==inf)
            cout << -1 << endl;
        else
            cout << d[b] << endl;
    }
    return 0;
}
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=205;
const int inf=0x3f3f3f3f;
int n,m,maze[maxn][maxn];

void floyed()
{
    for(int k=0;k<n;k++)
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            if(i!=j&&j!=k&&k!=i)
            maze[i][j]=min(maze[i][j],maze[i][k]+maze[k][j]);
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        int a,b,c;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            {
                if(i!=j)
                maze[i][j]=inf;
            }
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            maze[a][b]=min(c,maze[a][b]);//處理解決重邊問題;
            maze[b][a]=min(c,maze[b][a]);
        }
        scanf("%d%d",&a,&b);
        floyed();
        if(maze[a][b]==inf)
            cout << -1 << endl;
        else
            cout << maze[a][b] <<endl;
    }
    return 0;
}

//自我整理:最短路徑的這三個算法,就像BFS一樣;
//spfa:每次把發生更新的點作為當前到該點的最短路徑,都入隊列視為下一次遍歷開始的源點;
//dijkstra:每次只取所有發生更新的點,即所有當前最短路徑中裏離源點最近的點作為下一次開始的源點,前面的值都視為已確定的最短路徑

最短路(Floyed、Dijkstra、Bellman-Ford、SPFA)