hdu 2544 最短路(最短路径)(flody、dij、dij+priority queue、bellman、spfa)

最短路

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 20581    Accepted Submission(s): 8811


Problem Description
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

 

Input
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。
 

Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间
 

Sample Input
  
  
2 1 1 2 3 3 3 1 2 5 2 3 5 3 1 2 0 0
 

Sample Output
  
  
3 2
 

Source
 

Recommend
lcy

题解:最短路径....下面给出部分最短路径解法以作总结(flody、dij、dij+priority queue、bellman、spfa)


flody算法:邻接矩阵存图,用3重for循环对枚3个点,以2条路径不断构成1条更短的路径
//flody算法
#include<stdio.h>
#include<string.h>
#define INF 1<<29
int c[105][105],n;
void init()
{
    int i,j;

    for(i=0;i<=n;i++)
        for(j=0;j<=n;j++)
            c[i][j]=i==j?0:INF;
}
void flody()
{
    int i,j,k;

    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
        if(c[i][j]>c[i][k]+c[k][j])
        c[i][j]=c[i][k]+c[k][j];
}
int main()
{
    int x,y,z,m,i;

    while(scanf("%d%d",&n,&m)>0)
    {
        if(n==0&&m==0) break;
        init();
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            c[x][y]=c[y][x]=z;
        }
        flody();
        printf("%d\n",c[1][n]);
    }

    return 0;
}


裸dij算法:邻接矩阵or邻接表存图,每次提取出未更新过且距离起点最近的点,标记该点,并通过该点更新其他点到起点的距离,每一个点只更新一次,一共更新n次
//裸dij算法
#include<stdio.h>
#include<string.h>
#define INF 1<<28
int c[105][105],l[105],mark[105],n;
void init()
{
    int i,j;

    for(i=0;i<=n;i++)
        for(j=0;j<=n;j++)
            c[i][j]=INF;
    for(l[1]=0,i=2;i<=n;i++) l[i]=INF;
    memset(mark,0,sizeof(mark));
}
void dij()
{
    int i,j,m,make;

    for(i=0;i<n;i++)
    {
        for(m=INF,j=1;j<=n;j++)
        {
            if(!mark[j]&&l[j]<m)
            make=j,m=l[j];
        }
        mark[make]=1;
        for(j=1;j<=n;j++)
        {
            if(!mark[j]&&c[make][j]<INF&&l[j]>l[make]+c[make][j])
                    l[j]=l[make]+c[make][j];
        }
    }
}
int main()
{
    int x,y,z,m,i;

    while(scanf("%d%d",&n,&m)>0)
    {
        if(n==0&&m==0) break;
        init();
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            c[x][y]=c[y][x]=z;
        }
        dij();
        printf("%d\n",l[n]);
    }

    return 0;
}


dij+priority queue优化:邻接矩阵or邻接表存图,在提取未更新过且最近的点的时候利用优先队列来提取,标记该点,且对每个可进行更新的点入队,这个点必定未更新过,直到优先队列为空则停止更新
//优先队列dij
#include<stdio.h>
#include<string.h>
#include<queue>
#define INF 1<<28
using namespace std;
struct point{
    int id,dis;
}cc;
bool operator <(const point &a,const point &b)
{
    return b.dis<a.dis;
}
priority_queue<point>q;
int c[105][105],l[105],mark[105],n;
void init()
{
    int i,j;

    for(i=0;i<=n;i++)
        for(j=0;j<=n;j++)
            c[i][j]=INF;
    for(l[1]=0,i=2;i<=n;i++) l[i]=INF;
    memset(mark,0,sizeof(mark));
}
void dij()
{
    int i,x;

    cc.id=1,cc.dis=0;
    q.push(cc);
    while(!q.empty())
    {
        cc=q.top();
        q.pop();
        x=cc.id;
        mark[x]=1;

        for(i=1;i<=n;i++)
        {
            if(!mark[i]&&l[i]>l[x]+c[i][x])
            {
                l[i]=l[x]+c[i][x];
                cc.id=i,cc.dis=l[i];
                q.push(cc);
            }
        }
    }
}
int main()
{
    int x,y,z,m,i;

    while(scanf("%d%d",&n,&m)>0)
    {
        if(n==0&&m==0) break;
        init();
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            c[x][y]=c[y][x]=z;
        }
        dij();
        printf("%d\n",l[n]);
    }

    return 0;
}


bellman:可以计算有负权,用普通的结构体将所有边存起来。对所有的边进行n-1次松弛更新点的最短距离,若某一次松弛已经不能更新,则可以退出循环,若n-1次更新后仍然能继续更新,则存在负权。
//bellman算法
#include<stdio.h>
#include<string.h>
#define INF 1<<28
int p[105],n;
int dis[105],cou;
struct edge
{
    int x,y,l;
}v[20005];
void init()
{
    int i=2;

    for(;i<=n;i++) dis[i]=INF;
    cou=dis[1]=0;
}
void add(int x,int y,int z)
{
    v[cou].x=x;
    v[cou].y=y;
    v[cou++].l=z;
}
void bellman()
{
    int flag=1,i,j;

    for(j=0;j<n-1&&flag;j++)
    {
        flag=0;
        for(i=0;i<cou;i++)
        {
            if(dis[v[i].x]+v[i].l<dis[v[i].y])
            {
                flag=1;
                dis[v[i].y]=dis[v[i].x]+v[i].l;
            }
        }
    }
}
int main()
{
    int m,i,x,y,z;

    while(scanf("%d%d",&n,&m)>0)
    {
        if(n==0&&m==0) break;
        init();
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);
            add(y,x,z);
        }
        bellman();
        printf("%d\n",dis[n]);
    }
}


spfa算法:可计算负权,由于一般不用该算法处理稠密图,所以一般用邻接表存图。由起点入队开始,每一次从队列出一个点,由该点出发更新其他点,对于每一个被更新且未入队的点都使其进入队列,每一个点可多次入队但不可以同时出现在队列2次,直到队列为空。
//spfa算法
#include<stdio.h>
#include<string.h>
#define INF 1<<28
int p[105],inse,n;
int dis[105],que[20005],mark[105];
struct linjiebiao
{
    int data,l,next;
}v[10005];
void init()
{
    int i;
    for(i=0;i<=n;i++)
    {
        p[i]=-1;
        dis[i]=INF;
        mark[i]=0;
    }
    dis[1]=0;
}
void add(int x,int y,int z)
{
    v[inse].next=p[x];
    v[inse].data=y;
    v[inse].l=z;
    p[x]=inse++;
}
void spfa()
{
    int sta=0,fin=1,temp,x;

    que[0]=mark[0]=1;
    while(sta<fin)
    {
        x=que[sta++];
        mark[x]=0;
        temp=p[x];
        while(temp!=-1)
        {
            if(dis[v[temp].data]>dis[x]+v[temp].l)
            {
                dis[v[temp].data]=dis[x]+v[temp].l;
                if(!mark[v[temp].data])
                {
                    que[fin++]=v[temp].data;
                    mark[v[temp].data]=1;
                }
            }
            temp=v[temp].next;
        }
    }
}
int main()
{
    int m,i,x,y,z;

    while(scanf("%d%d",&n,&m)>0)
    {
        if(n==0&&m==0) break;
        init();
        for(inse=i=0;i<m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z),add(y,x,z);
        }
        spfa();
        printf("%d\n",dis[n]);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值