HDU2544最短路(边权为正,三种最短路算法解决)

第一种做法: djkstra做法

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=105;
const int inf=0x3f3f3f;
int mapp[maxn][maxn];
int vis[maxn];//区分结点在S中还是V-S中
int dis[maxn];//最短路
int n,m,a,b,c;
void init()
{
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            mapp[i][j]=inf;
    memset(vis,0,sizeof(vis));
}
void djkstra(int x,int n)//以x为源点,以n为终点的最短路
{
    for(int i=1; i<=n; i++)
        dis[i]=mapp[x][i];
    vis[x]=1;//S中初始的点只有x结点
    for(int i=1; i<=n; i++)
    {
        int minx=inf;
        int p;
        for(int j=1; j<=n; j++)
            if(!vis[j]&&dis[j]<minx)
            {
                p=j;
                minx=dis[j];
            }//将V-S中的最短路径求出,用dis[p]表示
        vis[p]=1;//使p点进入S集合
        for(int j=1;j<=n;j++)
            if(!vis[j]&&dis[j]>dis[p]+mapp[p][j])
            dis[j]=dis[p]+mapp[p][j];
    }
}
int main()
{
    while(cin>>n>>m)
    {
        if(!m&&!n)break;
        init();
        for(int i=0; i<m; i++)
        {
            cin>>a>>b>>c;
            mapp[a][b]=mapp[b][a]=c;
        }
        djkstra(1,n);
        cout<<dis[n]<<endl;
    }
    return 0;
}

第二种做法:弗洛伊德做法

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=105;
const int inf=0x3f3f3f;
int dis[maxn][maxn];
int n,m,p,q,t;
void init()
{
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            dis[i][j]=inf;
}
void floyd()
{
    for(int k=1; k<=n; k++)
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
int main()
{
    while(cin>>n>>m)
    {
        if(n==0&&m==0)
            break;
        init();
        for(int i=0; i<m; i++)
        {
            cin>>p>>q>>t;
            dis[p][q]=dis[q][p]=t;
        }
        floyd();
        cout<<dis[1][n]<<endl;
        //已经求得任意两点间的最短路径
    }
    return 0;
}

总结:djkstra算法的求解过程:

对于网络N=(V,E)中的顶点V划分为两部分:

第一部分S:表示已经求出最短路径的顶点集合,初始时只包含源点v0

第二部分V-S:没有求出最短路径的顶点集合,初始时为集合V-{v0}

算法的操作内容:按照v0到每个顶点最短路径长度递增的顺序,将集合V-S中的顶点逐个加入S中。

在操作的过程中,总保证v0到S中各顶点的长度始终不大于v0到V-S中各顶点的路径长度。

下面证明这种求解方式的正确性:

假设S是已经求得最短路径顶点的集合,假设下一条我们要求的最短路径终点是x

可以证明:下一条最短路径只有下面两种情况而不会出现第三种情况:

1.边(v0,x)

2.v0经过S集合中的顶点最后到达x的路径

我们可以用反证法证明:假设出现第三种情况:v0经若干顶点到达x的最短路径上有一个顶点v在集合V-S中:

这说明存在一个V-S集合中的顶点v使v0-v的路径长度小于v0-x的路径长度,这是不可能的。因为算法是按照

路径长度递增的顺序产生最短路径的,所以长度比v0-x小的路径都已经产生,且顶点v必定在集合S中。

算法的正确性证明完毕。

 第三种做法:SPFA

这种算法以队列的形式改善了Bellman-frod算法,降低了时间复杂度,写法上和BFS类似,不过需要结点出队后标记数组重新赋值为0

利用访问邻接点的方式进行对其他结点的更新,进行松弛操作,直到所有节点都达到最短路径为止

下面是AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=105;
const int inf=0x3f3f3f3f;
int mapp[maxn][maxn];
int dis[maxn];
int vis[maxn];
int n,m;
queue<int>que;
void init()
{
    memset(vis,0,sizeof(vis));
    memset(mapp,inf,sizeof(mapp));
    memset(dis,inf,sizeof(dis));
}
void spfa()
{
    vis[1]=1;
    dis[1]=0;
    que.push(1);
    while(!que.empty())
    {
        int pp=que.front();
        que.pop();
        vis[pp]=0;//由于结点不一定达到最短路径,所以标志数组需要赋值为0
        for(int i=1; i<=n; i++)
            if(dis[i]>dis[pp]+mapp[pp][i])
            {
                dis[i]=dis[pp]+mapp[pp][i];
                if(!vis[i])
                {
                    que.push(i);
                    vis[i]=0;
                }
            }
    }
}
int main()
{
    int a,b,t;
    while(scanf("%d%d",&n,&m),n+m)
    {
        init();
        for(int i=0; i<m; ++i)
        {
            scanf("%d%d%d",&a,&b,&t);
            if(t<mapp[a][b])
                mapp[a][b]=mapp[b][a]=t;
        }
        spfa();
        printf("%d\n",dis[n]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41658955/article/details/81480796
今日推荐