PAT 1030 Travel Plan(單源最短路徑+優化Dijkstra)

題目

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同時維護cost
int 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章