求解单源最短路径:Dijkstara算法典型示例

来源:互联网 发布:org.apache jar包 编辑:程序博客网 时间:2024/05/22 05:00

求解单源最短路径:Dijkstara算法典型示例(PAT甲级1087)

一、问题描述

dijkstara是解决单源最短路径的典型算法。即给你 G = < V , E > (图:顶点与边集),让你求解某点到其他点的最短距离。一般来说,我们看到的示例程序是指输出最小距离。本典型示例中所要输出的比较全面,假设到达每个点可以获得x价值的东西。这个程序我们可以求出:最短距离、满足短距离有多少种走的方法、可以得到的最大的价值、所经过的边的数目、最优方式走过的路径。本算法基于PAT甲级的1087,个人思考了很久,然后在网上看到了一篇优秀的博客,参悟了一会儿,结合了自己的想法,总结了一些。

二、原题

1087. All Roads Lead to Rome (30)

时间限制
200 ms
内存限制
65536 kB
代码长度限制
16000 B
判题程序
Standard
作者
CHEN, Yue

Indeed there are many different tourist routes from our city to Rome. You are supposed to find your clients the route with the least cost while gaining the most happiness.

Input Specification:

Each input file contains one test case. For each case, the first line contains 2 positive integers N (2<=N<=200), the number of cities, and K, the total number of routes between pairs of cities; followed by the name of the starting city. The next N-1 lines each gives the name of a city and an integer that represents the happiness one can gain from that city, except the starting city. Then K lines follow, each describes a route between two cities in the format "City1 City2 Cost". Here the name of a city is a string of 3 capital English letters, and the destination is always ROM which represents Rome.

Output Specification:

For each test case, we are supposed to find the route with the least cost. If such a route is not unique, the one with the maximum happiness will be recommended. If such a route is still not unique, then we output the one with the maximum average happiness -- it is guaranteed by the judge that such a solution exists and is unique.

Hence in the first line of output, you must print 4 numbers: the number of different routes with the least cost, the cost, the happiness, and the average happiness (take the integer part only) of the recommended route. Then in the next line, you are supposed to print the route in the format "City1->City2->...->ROM".

Sample Input:
6 7 HZHROM 100PKN 40GDN 55PRS 95BLN 80ROM GDN 1BLN ROM 1HZH PKN 1PRS ROM 2BLN HZH 2PKN GDN 1HZH PRS 1
Sample Output:
3 3 195 97HZH->PRS->ROM

三、问题解释

即告诉你有6个城市、7条道路,起点是HZH,下面5个是到达这些城市能够获得的幸福值,再下面7行表示两个城市之间的道路长度,最后需要到达罗马(ROM)。

输出分别为:最短距离是多少、满足短距离有多少种走的方法、可以得到的最大的价值、所经过的边的数目、最优方式走过的路径。

四、关键思路与解决方案

(1)字符串存储问题

map<string,int> city;//字符串对应数字
map<int,string> rcity;//数字对应字符串

我们采用map方式来映射这种关系,相信大家也学过STL里面的内容,进行使用时,采用字符串对应数字的map;进行输出时,采用数字对应字符串的map。

(2)存储图

map<int,vector<pair<int,int> > > edge;

可能大家对这种存储方法有点陌生,但是其实也很好理解,这种方法有效地避免了空间的浪费。每一行的元素长度只与其度相关。即其长度为vector<pair<int , int> >的长度,那么,pair又是什么意思呢?其实也很容易理解,把它看成有两个元素的数组好了。本程序中,pair中的两个元素分别表示:所要连接的城市的下标,两个城市之间的道路长度。

因此,每次进行输入的时候,只要寻找到下标,再把数据转换成pair后放进vector即可。

cin>>u>>v;
scanf("%d",&d);
edge[city[u]].push_back(make_pair(city[v],d));
edge[city[v]].push_back(make_pair(city[u],d));

(3)初始化方法

int dis[205],path[205],hcount[205],happ[205],fstep[205],f[205],vis[205];
//最小距离、最小距离有多少种、到该点的最大幸福感、每个点的幸福值、到每个点最短距离的经过路径数、每个点前驱坐标、是否访问int n,k;//城市数、道路数。
scanf("%d%d",&n,&k);//输入城市数和道路数
 for(int i=0;i<n;i++)
 {
    dis[i]=99999999;
    hcount[i]=-1;
    vis[i]=0;
    path[i]=0;
    fstep[i]=0;
}
    cin>>st;
    city[st]=0;//编号
    rcity[0]=st;//反编号
    happ[0]=0;
    dis[0]=0;
    hcount[0]=0;
    fstep[0]=0;
    path[0]=1;
    f[0]=0;

(4)迭代器的使用

    s=0;//初始点为0
    vector<pair<int,int> >::iterator it;//迭代器
    int next;//设置下一个坐标

由迭代器进行每一个遍历操作。

(5)更新操作

更新操作需要几步讨论

1)新的距离较短

                dis[next]=dis[s]+it->second;
                hcount[next]=hcount[s]+happ[next];//到达next这一点的幸福感总和
                path[next]=path[s];//记录路径,前一个点的path值与之相等
                fstep[next]=fstep[s]+1;//到这一点需要几步
                f[next]=s;//下一个点的上一坐标

2)距离相等,但是新的方法幸福感更高

                hcount[next]=hcount[s]+happ[next];//更新后的辛福感
                fstep[next]=fstep[s]+1;//步骤增一
                f[next]=s;//总步数

3)新的方法幸福感一样,但是到这个城市需要的步骤较少

                fstep[next]=fstep[s]+1;
                f[next]=s;

(6)新的点的选取

在没有访问过的顶点中寻找距离最短的点即可

      int mindis=99999999,mininum=1;
        for(int i=1; i<n; i++)//新的点的选取
        {
            if(!vis[i]&&dis[i]<mindis)//选取没有访问过的dis的最小值,即dijstar
            {
                mindis=dis[i];
                mininum=i;
            }
        }
        s=mininum;

(7)路径输出

采用栈的方式输出(先入后出)

    int p=s;//最后的点
    stack<int> ss;//定义栈
    while(p)
    {
        ss.push(p);
        p=f[p];//p更新为它的前驱节点
    }
    cout<<rcity[p];
    while(!ss.empty())//输出
    {
        cout<<"->"<<rcity[ss.top()];//依次从新到旧输出顶点
        ss.pop();
    }
    cout<<endl;

五、代码实现(个人希望大家自己按照上面的部分代码去实现本程序,后面的代码仅供结果参考)

#include <iostream>#include <cstdio>#include <stack>#include <algorithm>#include <map>#include <vector>#include <cstring>using namespace std;map<string,int> city;//字符串对应数字map<int,string> rcity;//数字对应字符串map<int,vector<pair<int,int> > > edge;//边,数字对应起始点,pair<int,int>为连接的城市与连接的两个城市的距离//最小距离、最小距离有多少种、到该点的最大幸福感、每个点的幸福值、到每个点最短距离的经过路径数、每个点前驱坐标、是否访问int dis[205],path[205],hcount[205],happ[205],fstep[205],f[205],vis[205];int n,k;//城市数、道路数int main(){    //临时变量    int d,s;    string u,st,v;    scanf("%d%d",&n,&k);//输入城市数和道路数    for(int i=0;i<n;i++)    {        dis[i]=99999999;        hcount[i]=-1;        vis[i]=0;        path[i]=0;        fstep[i]=0;    }    cin>>st;    city[st]=0;//编号    rcity[0]=st;//反编号    happ[0]=0;    dis[0]=0;    hcount[0]=0;    fstep[0]=0;    path[0]=1;    f[0]=0;    for(int i=1; i<n; i++)//输入城市的幸福感    {        f[i]=i;        cin>>u;        rcity[i]=u;        city[u]=i;        scanf("%d",&happ[i]);    }    for(int i=0; i<k; i++)//输入两个城市之间的距离    {        //将u和v转换为下标,在相应的map中进行边的添加pair        cin>>u>>v;        scanf("%d",&d);        edge[city[u]].push_back(make_pair(city[v],d));        edge[city[v]].push_back(make_pair(city[u],d));    }    s=0;//初始点为0    vector<pair<int,int> >::iterator it;//迭代器    int next;//设置下一个坐标    while(s!=city["ROM"])//s不是终点    {        vis[s]=1;//设置为已访问        for(it=edge[s].begin(); it!=edge[s].end(); it++)//遍历s的所有邻接点        {            next=it->first;//first为邻接点的位置            if(!vis[next]&&dis[next]>dis[s]+it->second)//距离更小,直接更新            {                dis[next]=dis[s]+it->second;                hcount[next]=hcount[s]+happ[next];//到达next这一点的幸福感总和                path[next]=path[s];//记录路径,前一个点的path值与之相等                fstep[next]=fstep[s]+1;//到这一点需要几步                f[next]=s;//下一个点的上一坐标            }            else            {                if(dis[next]==dis[s]+it->second)//两个路径的花费相同时进行进一步的操作                {                    path[next]+=path[s];                    if(hcount[next]<hcount[s]+happ[next])//更新后的幸福感较高                    {                        hcount[next]=hcount[s]+happ[next];//更新后的辛福感                        fstep[next]=fstep[s]+1;//步骤增一                        f[next]=s;//总步数                    }                    else                    {                        if(hcount[next]==hcount[s]+happ[next])//幸福感相同                        {                            if(fstep[next]>fstep[s]+1)//但是步骤更少,更新                            {                                fstep[next]=fstep[s]+1;                                f[next]=s;                            }                        }                    }                }            }        }        int mindis=99999999,mininum=1;        for(int i=1; i<n; i++)//新的点的选取        {            if(!vis[i]&&dis[i]<mindis)//选取没有访问过的dis的最小值,即dijstar            {                mindis=dis[i];                mininum=i;            }        }        s=mininum;    }    printf("%d %d %d %d\n",path[s],dis[s],hcount[s],hcount[s]/fstep[s]);//最小花费有多少种,最小花费的值,幸福感总值,幸福感平均值    int p=s;    stack<int> ss;//定义栈    while(p)    {        ss.push(p);        p=f[p];//p更新为它的前驱节点    }    cout<<rcity[p];    while(!ss.empty())//输出    {        cout<<"->"<<rcity[ss.top()];        ss.pop();    }    cout<<endl;    return 0;}

0 0
原创粉丝点击