PTA刷題Advanced甲級——1003.Emergency——Day(2)

題目描述

在這裏插入圖片描述簡單翻譯一下題目:
給出N個城市,M條無向邊,每個城市都有一定數目的救援小組,所有邊的邊權已知。現在給出起點和終點,求從起點到終點的最短路徑條數及最短路徑上的救援小組數目之和。如果有多條最短路徑,則輸出數目之和最大的。

題目分析

由於我們的起點是固定的,所以本題就是求解出從起點到終點的最短距離,很明顯這是一個單源最短路徑問題,即Dijkstra算法。
但是由於每一個頂點都具有一個權重,對應於題中所說的每個城市有一定數量的搜救隊員。我們需要找到一條最短路徑且可以蒐集到最多搜救隊員的路徑,即這條路徑上經過的點權重和最大。
標準的Dijsktra算法解題,但是我們需要開闢兩個數組分別記錄點權和到達某點的最大點權和。接下來就是本題中最容易忽視的點,題目要求我們找出最短路徑條數而不是最短路徑長度。
在這裏插入圖片描述
我們可以看到從V0到V2,可以有兩條最短路徑:1.V0->V2 2.V0->V1->V2
這兩條最短路徑長度都是2,樣例中的答案也給出的是2,所以這會讓大家產生混淆,以爲題目要求我們輸出的是最短路徑長度。
因此,我們也需要單獨開闢一個數組來記錄最短路徑條數,怎麼更新這個數組中的值呢?
我們假設起點爲s,終點爲v,中介點爲u,如果從s直接到達v的距離 > 從s到u再到v的距離和的話,我們就將最小距離更新,並且用num[u]來更新num[v],這條最短路徑的條數應該是和從s到u的最短路徑條數的相同的。
如果從s直接到達v的距離 = 從s到u再到v的距離和的話,我們就應該將num[v]+=num[u],此時又遇到了相同最短路徑,條數應該+num[u]。
最終我們輸出num[終點]和w[終點] (w是點權數組)。

代碼

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX = 510;//最多結點數
const int INF = 100000000;
int st,ed,n,m;//起點,終點,點數,邊數
int G[MAX][MAX];//鄰接矩陣
int num[MAX];//num[i]記錄到達i點的最短路徑條數
int weight[MAX],w[MAX],d[MAX];//點權,w[i]表示到達點i時的最大點權,d[i]表示從起點到點i的最短距離
bool vis[MAX] = {false};//記錄某個結點是否被訪問過,初始都未訪問過
void Dijkstra(int s)//s爲起點
{
    memset(num,0,sizeof(num));
    memset(w,0,sizeof(w));
    fill(d,d+MAX,INF);
    num[s] = 1;
    d[s] = 0;
    w[s] = weight[s]; 
    for(int j = 0;j < n;j++)
    {
        int u = -1;//記錄當前距離最近的結點序號
        int min = INF;
        for(int i = 0;i < n;i++)
        {
            if(vis[i]==false && d[i] < min)
            {
                u = i;
                min = d[i];
            }
        }
        if(u == -1)
            return;
        vis[u] = true;
        for(int k = 0;k < n;k++)
        {
            //如果k未被訪問且u可以到達k且以u爲中介點可以使d[k]更優
            if(G[u][k]!=INF&&vis[k]==false)
            {
                if(d[u]+G[u][k]<d[k])
                {
                    d[k] = d[u]+G[u][k];
                    w[k] = w[u]+weight[k];
                    num[k] = num[u];
                }
                else if(G[u][k]+d[u]==d[k])
                {
                    if(weight[k]+w[u] > w[k])//以u爲中介點可以形成更大的點權
                        w[k] = weight[k]+w[u];
                    num[k] += num[u];//此時遇到了一條最短長度相等的路徑,要在原有基礎上加上num[u]
                }
            }
        }
    }
}
int main()
{
    scanf("%d %d %d %d",&n,&m,&st,&ed);//輸入邊數,點數,起點和終點
    for(int i = 0;i < n;i++)
    {
        scanf("%d",&weight[i]);
    }
    int u,v;//兩個相連的頂點
    fill(G[0],G[0]+MAX*MAX,INF);//初始化圖G
    for(int i = 0;i < m;i++)
    {
        scanf("%d %d",&u,&v);
        scanf("%d",&G[u][v]);
        G[v][u] = G[u][v];
    }
    Dijkstra(st);
    printf("%d %d\n",num[ed],w[ed]);//最短距離條數,最短路徑中的最大點權
    return 0;
}

答題用時19min
Q3——finish√

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章