PAT 1030 Travel Plan(单源最短路径+优化Dijkstra)

来源:互联网 发布:手机磁盘清理软件 编辑:程序博客网 时间:2024/06/10 22:40

题目

https://www.patest.cn/contests/pat-a-practise/1030

题意:给出每座城市之间高速公路的长度和花费,求从给定起点到终点的最短路径并输出,若有多条最短路径,记录花费最小的那条。

解题思路

本题是单源最短路径的简单变形。不仅要考虑距离,还要考虑花费。

关于单源最短路径,详见之前的一篇博客:PAT 1003 Emergency(单源最短路径+Dijkstra)

对算法的修改如下:

  • 在维护dist数组的同时,还要维护cost数组,其意义与dist基本相同。
  • 松弛时要考虑的情况:

    • 通过pos到k的距离更短
    • 通过pos到k的距离相等时,花费更小。

    出现上述两种情况时,要同时更新dist和cost数组。

AC代码

优先队列优化过的Dijkstra

#include <iostream>#include <cstdio>#include <queue>#include <algorithm>using namespace std;typedef pair<int, int> P; ///first是距离,second是编号const int maxn = 505, INF = 1<<27;int graph[maxn][maxn], weight[maxn][maxn];int dist[maxn], cost[maxn]; //dist、cost表示当前点距源点最短距离(花费)bool visit[maxn];int pre[maxn]; //记录前驱顶点int n, m, src, dest;void init() //用fill初始化,比memset功能更强,注意格式{    fill(graph[0], graph[0]+maxn*maxn, INF); //二维数组形式    fill(weight[0], weight[0]+maxn*maxn, INF); //一维数组形式    fill(visit, visit+maxn, false); //布尔形式    fill(dist, dist+maxn, INF);    fill(cost, cost+maxn, INF);}void Dijkstra(int s) ///优先队列优化的dijkstra算法{    dist[s] = cost[s] = 0;    priority_queue<P, vector<P>, greater<P> > q; //按照pair的第一个值升序    q.push(P(0, s));    while(!q.empty())    {        P out = q.top();        q.pop();        int pos = out.second; //顶点编号        if (visit[pos])            continue;        //从pos开始松弛        visit[pos] = true; //标记访问        for (int i = 0; i < n; ++i)        {            if (!visit[i] && dist[pos] + graph[pos][i] <= dist[i]) //通过pos到i路径可能会更短            {                if ( dist[pos] + graph[pos][i] < dist[i] ||                     (dist[pos] + graph[pos][i] == dist[i] && cost[pos] + weight[pos][i] < cost[i]) ) //路径更短或路径长度相等但花费更少                {                    pre[i] = pos; //更新前驱顶点                    dist[i] = dist[pos] + graph[pos][i];                    cost[i] = cost[pos] + weight[pos][i];                    q.push(P(dist[i], i)); //加入更新后的路径值和顶点编号                }            }        }    }}int main(){    scanf("%d %d %d %d", &n, &m, &src, &dest);    init(); //初始化    int from, to, dis, cos;    for (int i = 0; i < m; ++i)    {        scanf("%d %d %d %d", &from, &to, &dis, &cos);        graph[from][to] = graph[to][from] = dis;        weight[from][to] = weight[to][from] = cos;    }    Dijkstra(src);    //输出路径    int path[maxn], cnt = 0, tmp = dest;    while (tmp != src)    {        path[cnt++] = tmp;        tmp = pre[tmp];    }    path[cnt++] = src;    for (int i = cnt-1; i >= 0; --i)        printf("%d ", path[i]);    printf("%d %d\n", dist[dest], cost[dest]);    return 0;}

小技巧:
STL中的泛型函数fill功能比memset更强大,memset只能对字节赋值,而fill则是任何值都可以,调用规则:
fill(first,last,val),其中first 为容器的首迭代器,last为容器的末迭代器,last为将要替换的值,注意二维数组a[x][y]的首尾迭代器要写a[0]而不是a。

未优化的Dijkstra

#include <iostream>#include <algorithm>using namespace std;const int maxn = 505, INF = 0X3f3f3f3f;int map[maxn][maxn], weight[maxn][maxn];int dist[maxn], cost[maxn]; //维护dist同时维护costint pre[maxn]; //记录前驱结点bool visited[maxn];int n;void init(){    for (int i = 0; i<n; ++i)    {        for (int j = 0; j<n; ++j)        {            map[i][j] = (i==j)? 0:INF;            weight[i][j] = (i==j)? 0:INF;        }        visited[i] = false;        pre[i] = -1;    }}void dijkstra(int st){    for (int i = 0; i<n; ++i)    {        dist[i] = map[st][i];        cost[i] = weight[st][i]; //更新cost        if (dist[i] != INF)            pre[i] = st;    }    visited[st] = true;    for (int i = 0; i < n-1; ++i)    {        int pos = -1, minn = INF;        //找到dist当前最小值        for (int j = 0; j < n; ++j)        {            if (!visited[j] && dist[j] < minn)            {                minn = dist[j];                pos = j;            }        }        visited[pos] = true; //标记pos被访问过        //松弛操作        for (int k = 0; k < n; ++k)        {            if (!visited[k] && dist[pos] + map[pos][k] <= dist[k])            {                //经过pos到k的距离更短,或距离相同代价更小                if (dist[pos] + map[pos][k] < dist[k] || ((dist[pos] + map[pos][k] ==  dist[k]) && cost[pos] + weight[pos][k] < cost[k]))                {                    dist[k] = dist[pos] + map[pos][k]; //更新dist                    cost[k] = cost[pos] + weight[pos][k]; //更新cost                    pre[k] = pos;                }            }        }    }}int main(){    ios::sync_with_stdio(false);    int m, start, dest;    cin >> n >> m >> start >> dest;    init();    int t1, t2, d, w;    for (int i = 0; i < m; ++i)    {        cin >> t1 >> t2 >> d >> w;        map[t1][t2] = map[t2][t1] = d;        weight[t1][t2] = weight[t2][t1] = w; //记录代价    }    dijkstra(start);    int along[maxn], cnt = 0;    int t = dest;    while (t != start) //记录所有前驱结点    {        along[cnt++] = t;        t = pre[t];    }    along[cnt++] = start; //加入起点    for (int i = cnt-1; i >= 0; --i)        cout << along[i] << ' ';    cout << dist[dest] << ' ' << cost[dest] << endl;    return 0;}
原创粉丝点击