最短路四种算法浅理解(大佬请无视)

第一种~~~~~~~~~~~~~~~~~dijkstra~~~~(加边法)

这个题目就是一个dijkstra模板题

poj2387

Til the Cows Come Home

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 71067   Accepted: 23743

Description

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

Hint

INPUT DETAILS:

There are five landmarks.

OUTPUT DETAILS:

Bessie can get home by following trails 4, 3, 2, and 1.

题目大意:贝茜想尽快从N回到到1号农场.

输入:第一行包括两个整数T和N,T表示有T条路线,N表示农场的数目,第二行到第T+1行每行三个数,第一个和第二个数是农场编号,第三个数是两个农场之间的距离.

思路:先用一个map数组把所有的路径存下来,不能到达的点赋值为无穷大(用99999之类的,用0x3f3f3f3f会wa(不知道为什么)),自己到自己赋值为0,用一个dis数组表示从第一个点到第i个点的最短距离.

dijkstra的思想:遍历dis数组每一次都去找最短的并且没有被标记过的一条边,找到之后标记这条边的点,然后用这一条边去松弛dis数组,一直到所有的点都被标记为止.

if(dp[v]+map1[v][j]<dp[j]&&vis[j]==0) {        dp[j]=dp[v]+map1[v][j]; }

这就是松弛的操作.

ac代码:

#include <iostream>
#include<cstdio>
using namespace std;
const int M=1005,inf=999999;
int map1[M][M],dp[M],vis[M],min1,v;
int main()
{
    int m,n;
    while(~scanf("%d%d",&m,&n))
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(i==j)
                    map1[i][j]=0;
                else
                    map1[i][j]=map1[j][i]=inf;
            }
            vis[i]=0;
        }
        for(int m1=1;m1<=m;m1++)
        {
            int a,b,c;
            cin>>a>>b>>c;
            if(c<map1[a][b])
            {
                map1[a][b]=map1[b][a]=c;
            }
        }
        for(int i=1;i<=n;i++)
        {
            dp[i]=map1[1][i];
        }
        for(int i=1;i<=n;i++)
        {
            min1=inf;
            for(int j=1;j<=n;j++)
            {
                if(vis[j]==0&&dp[j]<min1)
                {
                    min1=dp[j];
                    v=j;
                }
            }
            vis[v]=1;
            for(int j=1;j<=n;j++)
            {
                if(dp[v]+map1[v][j]<dp[j]&&vis[j]==0)
                {
                    dp[j]=dp[v]+map1[v][j];
                }
            }
        }
        cout<<dp[n]<<endl;
    }
}

dijkstra用于解决无负权边的时候比较适用,当有负权边的时候就需要用到接下来的三种算法了.

第二种~~~~~~~~~~~~~~~~~~~~~~~~~~~floyd~~~~~~(加点法)

先说一下思想:对于图中的每一个点,都把它当成中间点去松弛其他的任意两个点,直到所有的点都被当成过中间点来松弛所有的边,可以求出每任意两个点之间的最短距离(双向图或者是单向图或者负权无环图都适用).

例题:poj2253(最短路变形)

Frogger

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 57773   Accepted: 18129

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

题目大意:一只青蛙要从第一个点跳到第二个点,他希望有这样一条路,这条路上的最长的边在所有的路里面最短.

输入:一个整数n,表示点的个数,第2行到第n+1行两个数x,y.第1到第n个点的坐标

思路:把坐标转化成map图,按弗洛伊德的思想去找每两个点之间的最长的那条路,松弛操作改一下就好了

if(mapt[j][k]>max(mapt[j][i],mapt[i][k]))
{
    mapt[j][k]=max(mapt[j][i],mapt[i][k]);
}

ac代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<vector>
#include<set>
#include<cstdlib>
#include<map>
#include<deque>
#include<queue>
#include<list>
const int inf=0x3f3f3f3f;
const int MOD=1e9+7;
#define ll long long
#define ME0(x) memset(x,0,sizeof(x))
#define MEF(x) memset(x,-1,sizeof(x))
#define MEI(x) memset(x,inf,sizeof(x))
using namespace std;
int main()
{
    int n,x,y,l=0;
    double zb[205][2];
    double mapt[205][205];
    double di;
    while(cin>>n&&n)
    {
        l++;
        ME0(zb);
        MEI(mapt);
        for(int n1=1;n1<=n;n1++)
        {
            cin>>x>>y;
            zb[n1][0]=x;
            zb[n1][1]=y;
            for(int n2=1;n2<=n1;n2++)
            {
                di=sqrt((zb[n1][0]-zb[n2][0])*(zb[n1][0]-zb[n2][0])+(zb[n1][1]-zb[n2][1])*(zb[n1][1]-zb[n2][1]));
                mapt[n1][n2]=mapt[n2][n1]=di;
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                for(int k=1;k<=n;k++)
                {
                    if(mapt[j][k]>max(mapt[j][i],mapt[i][k]))
                    {
                        mapt[j][k]=max(mapt[j][i],mapt[i][k]);
                    }
                }
            }
        }
        cout<<"Scenario #"<<l<<endl;
        cout<<"Frog Distance = ";
        printf("%.3lf\n\n",mapt[1][2]);
    }
}

上述两种方法都是在邻接表的基础上实现的(不会的可以去了解一下),接下来的是在邻接表的情况下实现的

第三种~~~~~~~~~~~~~~~~~~~·bellman-ford

算法思路:首先设一个dis数组,dis[i]表示源点到i的最短距离,对于每一个节点遍历每一条边(start,end,distance)(这条边可能和这个节点无关,这就涉及到下一种算法了),如果有dis[start]+distance<dis[end]则松弛,即

if(dis[strat]+distance<dis[end])
{
    dis[end]=dis[strat]+distance;
}

最多进行(n-1)(n为节点个数)次即可,因为源点到源点的最短距离为0,当进行n-1次操作之后还能继续松弛,意味着存在负权回路,无最短路径.

第四种~~~~~~~~~~~~~~~~~~spfa(队列优化的bellman-ford)

首先介绍一下邻接表的操作

首先定义一个结构体,一个next数组,一个first数组,next数组和first数组初始化为0,first[n1]的意义是:以n1为起点(或终点,可自行调整)的最后一条边的编号(边按输入顺序由1到m),next[m1]的意义是:和第m1条边起点(或终点,可自行调整)相同的前一条边的编号

即在输入时

memset(next,0,sizeof(next));
memset(first,0,sizeof(first));
for(int m1=1;m1<=m;m1++)
{
    cin>>start>>end>>distance;
    lx[m1].st=start;lx[m1].ed=end;lx[m1].di=distance;
    next[m1]=first[lx[m1].st];
    first[lx[m1].st]=m1;
}

算法思路:设一个队列q,一个dis数组,一个flag数组,dis[i]表示源点start到i的最短距离(除源点start外初始化为无穷大),源点start入队,flag[start]=1,然后通过之前的next和first数组遍历所有以源点start为起点的边,进行松弛,如果能松弛且这条边的终点不在队列里,则把这个点入队(这条边被松弛之后入队可以用来松弛其他的点)

for(int i=first[start];i!=0;i=next[i])
{
    int y=lx[i].ed;
    if(dis[y]>dis[start]+lx[i].di)
    {
        dis[y]=dis[start]+lx[i].di;
        if(!flag[y])
        {
            q.push(y);
            flag[y]=1;
        }
    }
}

显然第一次时dis[i]为无穷大,所有和源点连接的点被松弛了,所以都入队了,接下来按队列一直循环松弛下去,直到队列为空,所有的路都是最短路时停止,就是bellman-ford算法遍历边时的优化,不会遍历无关的边,值遍历与这个节点相关的边,减少了时间消耗.

这个题可以用bellman-ford,dijkstra和spfa  我给出的是bellman-ford和spfa

poj1797

Heavy Transportation

Time Limit: 3000MS   Memory Limit: 30000K
Total Submissions: 46838   Accepted: 12192

Description

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

题目大意:现在有n个点,m条路,每条路有一个承重,找出一条从1到n的路,使这条路的承重最大(和小青蛙那题反过来了)

第一行输入一个数T表示测试个数,接下来一行输入两个数n和m,表示有n个点m条边.后面是m行,每行三个数,start起点,end终点,distance承重

ac代码

bellman-ford

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<vector>
#include<set>
#include<cstdlib>
#include<map>
#include<deque>
#include<queue>
#include<list>
const int inf=0x3f3f3f3f;
const int MOD=1e9+7;
#define ll long long
#define ME0(x) memset(x,0,sizeof(x))
#define MEI(x) memset(x,inf,sizeof(x))
using namespace std;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~bellman-ford
struct MAPT
{
    int st,ed,di;
}mapt[1000010];
int main()
{
    int t,n,m,dis[1005],s,e,d;
    scanf("%d",&t);
    for(int t1=1;t1<=t;t1++)
    {
        scanf("%d%d",&n,&m);
        ME0(dis);
        for(int m1=1;m1<=m;m1++)
        {
            scanf("%d%d%d",&s,&e,&d);
            mapt[m1].st=s;mapt[m1].ed=e;mapt[m1].di=d;
        }
        dis[1]=inf;
        for(int n1=1;n1<=n-1;n1++)
        {
            for(int m1=1;m1<=m;m1++)
            {
                if(dis[mapt[m1].ed]<min(dis[mapt[m1].st],mapt[m1].di))
                    dis[mapt[m1].ed]=min(dis[mapt[m1].st],mapt[m1].di);
                if(dis[mapt[m1].st]<min(dis[mapt[m1].ed],mapt[m1].di))
                    dis[mapt[m1].st]=min(dis[mapt[m1].ed],mapt[m1].di);
            }
        }
        printf("Scenario #%d:\n",t1);
        printf("%d\n\n",dis[n]);
    }
}

spfa

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<vector>
#include<set>
#include<cstdlib>
#include<map>
#include<deque>
#include<queue>
#include<list>
const int inf=0x3f3f3f3f;
const int MOD=1e9+7;
#define ll long long
#define ME0(x) memset(x,0,sizeof(x))
#define MEF(x) memset(x,-1,sizeof(x))
#define MEI(x) memset(x,inf,sizeof(x))
using namespace std;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~spfa
struct LX
{
    int st,ed,di;
}lx[1000010];
int next[1000010];
int first[1005];
int flag[1005];
int cnt;
int main()
{
    int t,m,n,st,ed,di,x,y,dis[1005];
    cin>>t;
    for(int t1=1;t1<=t;t1++)
    {
        cin>>n>>m;
        ME0(first);
        ME0(next);
        cnt=0;
        for(int m1=1;m1<=m;m1++)
        {
            cin>>st>>ed>>di;
            cnt++;
            lx[cnt].st=st;lx[cnt].ed=ed;lx[cnt].di=di;
            next[cnt]=first[lx[cnt].st];
            first[lx[cnt].st]=cnt;
            cnt++;
            lx[cnt].ed=st;lx[cnt].st=ed;lx[cnt].di=di;
            next[cnt]=first[lx[cnt].st];
            first[lx[cnt].st]=cnt;
        }
        ME0(flag);
        ME0(dis);
        dis[1]=inf;
        queue<int> q;
        q.push(1);
        flag[1]=1;
        while(!q.empty())
        {
            x=q.front();
            q.pop();
            flag[x]=0;
            for(int i=first[x];i!=0;i=next[i])
            {
                y=lx[i].ed;
                if(dis[y]<min(dis[x],lx[i].di))
                {
                    dis[y]=min(dis[x],lx[i].di);
                    if(!flag[y])
                    {
                        q.push(y);
                        flag[y]=1;
                    }
                }
            }
        }
        cout<<"Scenario #"<<t1<<":"<<endl;
        cout<<dis[n]<<endl;
        cout<<endl;
    }
}

目前只是浅见,水了几个简单题~~~~~~~~~~~~~~~~~~~~·

猜你喜欢

转载自blog.csdn.net/ecjtu_17_TY/article/details/81272562
今日推荐