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

来源:互联网 发布:企业淘宝开店流程 编辑:程序博客网 时间:2024/05/22 04:27

最短路

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 11 2 33 31 2 52 3 53 1 20 0
 

Sample Output
32
 

Source
UESTC 6th Programming Contest Online
 

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<<29int 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<<28int 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<<28using 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<<28int 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<<28int 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]);    }}


原创粉丝点击