1003 緊急【PAT(上級)演習】

1003 緊急【PAT(上級)演習】

元の質問のリンク:質問の詳細をプレビュー - 1003 緊急事態 (pintia.cn)

1.タイトル原文

都市の緊急救助チームのリーダーとして、あなたにはあなたの国の特別な地図が与えられます。地図には、いくつかの散在する都市がいくつかの道路でつながっていることが示されています。各都市の救助チームの数と、都市間の各道路の長さが地図上にマークされます。他の都市から緊急通報があった場合、あなたの仕事は部下をできるだけ早くその場所に誘導し、その間に途中でできるだけ多くの人たちを呼び寄せることです。

入力仕様:

各入力ファイルには 1 つのテスト ケースが含まれます。各テスト ケースの最初の行には 4 つの正の整数が含まれます: N N N ( ≤ 500 \and 500500) - 都市の数 (都市には 0 から N − 1 N-1 N1)、 M M M - 道路の数、 C 1 C_1 C1 および C 2 C_2 C2 - 現在いる都市と保存する必要がある都市。次の行には N N が含まれていますN 整数。 i i i 番目の整数は、 i i i 番目の都市。次に M M M 行が続き、それぞれが 3 つの整数で道路を記述します c 1 c_1 c1 c 2 c_2 c2 L L L は、それぞれ道路で接続されている都市のペアとその道路の長さです。 C 1 C_1 C1 から C 2 C_2 C2

出力仕様:

各テスト ケースについて、2 つの数値を 1 行に出力します。 C 1 C_1 間の異なる最短パスの数です。C1 および C 2 C_2 C2 、そしてあなたが集めることができる救助チームの最大数。行内のすべての数値は 1 つのスペースで区切る必要があり、行末に余分なスペースを含めることはできません。

サンプル入力:

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1

サンプル出力:

2 4

2. タイトル翻訳

都市の緊急救助チームのリーダーとして、あなたにはあなたの国の特別な地図が提供されます。地図には、いくつかの散在する都市がいくつかの道路でつながっていることが示されています。地図には、各都市の救助チームの数と、都市間の道路の長さがマークされています。他の都市から緊急通報を受けたとき、あなたの仕事は、途中でできるだけ多くの援助を呼びながら、チームをできるだけ早くその場所に導くことです。

入力仕様:

各入力ファイルには 1 つのテスト ケースが含まれます。各テスト ケースの最初の行には 4 つの正の整数が含まれます: N N N( ≤ 500 \le 500500) - 都市の数 (0 ~ N − 1 N-1 N1)、 M M M - 道路数量, C 1 C_1 C1 C 2 C_2 C2 - 現在いる都市と保存する必要がある都市。次の行には N N が含まれていますN个整数,其中第 i i i个整数是第 i i i 都市の救助チームの数。次に M M M 行。各行は道路を表し、3 つの整数が含まれます。 c 1 c_1 c1 c 2 c_2 c2 L L L はそれぞれ、道路によって接続されている都市のペアと道路の長さです。 C 1 C_1 C1 C 2 C_2 C2 のパス。

出力仕様:

テスト ケースごとに、1 行に 2 つの数値を出力します: C 1 C_1 C1 C 2 C_2 C2 最短経路の数と、集められる救助チームの最大数は異なります。行内のすべての数値はスペースで区切る必要があり、行の末尾に余分なスペースがあってはなりません。

入力例:

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1

出力例:

2 4

3. 問題解決のアイデア

3.1 質問分​​析

目的の都市に到達するための最短経路は何通りあるのか、またそれらの経路の中で呼び出せる救助隊の最大数はいくらなのかを計算します。

都市の数、道路の数、開始都市、目標都市、各都市の救助チームの数、都市間の道路の長さを入力します。

最短経路の数と、その経路に沿って召喚できる救助チームの最大数を出力します。

3.2 基本的な考え方

ダイクストラのアルゴリズムを使用して、単一ソースの最短パス問題を解決します。目標は、開始点から他のすべての頂点までの最短パスを見つけることです。この問題では、最短経路の数を求めることに加えて、最短経路が通過する都市の最大救助隊数を計算する必要もあります。

3.3 詳細な手順

  1. 初期化: グラフの隣接行列と相関配列を初期化します。開始点からそれ自体までの最短パスを 0 に設定し、他のすべての頂点の最短パスの長さを無限大に設定します。同時に、最短パスが決定されていない頂点を保存するために、コレクション (通常は優先キューまたは最小ヒープを使用して実装されます) が維持されます。

  2. 入力の読み取り: 都市の数 (n)、道路の数 (m)、開始点 (s)、目標点 (d)、およびそれぞれを読み取ります。都市内の救助チームの数と道路接続の関係。

  3. ダイクストラのアルゴリズム: ダイクストラのアルゴリズムを使用して、開始点から目標点までの最短経路の数と救助チームの最大数を計算します。各ステップで、現在の経路が最も短い頂点が選択され、その隣接する頂点の経路長が更新され、最短経路の数と救助チームの最大数が維持されます。

  4. 結果の出力: 最短経路の数と救助チームの最大数を出力します。

4.参考回答

#include <stdio.h>

#define MAXN 500
#define inf 1000000000//inf表示无穷大,即表示两城市之间无直接道路
typedef int elem_t;

//mat[MAXN][MAXN]: 二维数组,表示城市之间的道路长度的邻接矩阵。mat[i][j]存储了城市i到城市j之间的道路长度。
//min[MAXN]: 一维数组,表示从起点到每个城市的当前最短路径长度。min[i]存储了从起点到城市i的当前最短路径长度。
//pre[MAXN]: 一维数组,表示每个城市在最短路径中的前驱城市。pre[i]存储了在最短路径中城市i的前驱城市。
//store[MAXN]: 一维数组,表示每个城市的救援队数量。store[i]存储了城市i的救援队数量。

elem_t s, mat[MAXN][MAXN], min[MAXN], pre[MAXN], store[MAXN];

// 初始化图的邻接矩阵
void initialize(int n){
    
    
    int i, j;

    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            mat[i][j] = inf;//初始化所有城市距离无穷大,即无直接道路
}

// 读取城市的救援队信息和道路的长度
void read_store_matrix(int n, int m) {
    
    
    int i, j, k;

    // 初始化图的邻接矩阵
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            mat[i][j] = inf;

    // 读取每个城市的救援队数量
    for (i = 0; i < n; i++)
        scanf("%d", &store[i]);

    // 读取道路的连接关系和长度
    for (k = 0; k < m; k++) {
    
    
        scanf("%d %d", &i, &j);
        scanf("%d", &mat[i][j]);
        mat[j][i] = mat[i][j];  // 由于是无向图,道路是双向的,因此对称赋值
    }
}


// 使用Dijkstra算法计算最短路径和最大救援队数量
void dijkstra(int n, elem_t d){
    
    
    int v[MAXN], cnt[MAXN], jb[MAXN];
    int i, j, k;

    // 初始化数组
    for (i = 0; i < n; i++)
        min[i] = inf, v[i] = 0, cnt[i] = 0, jb[i] = store[i], pre[i] = -1;
    
    // 起点的路径长度为0,起点被标记为已确定最短路径
    cnt[s] = 1;

    for (min[s] = 0, j = 0; j < n; j++){
    
    
        // 选择当前路径最短的顶点
        for (k = -1, i = 0; i < n; i++)
            if (!v[i] && (k == -1 || min[i] < min[k]))
                k = i;

        // 标记当前顶点为已确定最短路径
        v[k] = 1;

        // 更新当前顶点的邻接顶点的路径长度
        for (i = 0; i < n; i++)
            if (!v[i]) {
    
    
                if (min[k] + mat[k][i] < min[i]) {
    
    
                    // 更新最短路径和最大救援队数量
                    min[i] = min[k] + mat[pre[i] = k][i];
                    cnt[i] = cnt[k];
                    jb[i] = jb[k] + store[i];
                }
                else if (min[k] + mat[k][i] == min[i]) {
    
    
                    // 如果有多条最短路径,累加路径数量和更新最大救援队数量
                    cnt[i] += cnt[k];
                    if (jb[k] + store[i] > jb[i]) 
                        jb[i] = jb[pre[i] = k] + store[i];
                }
            }
    }

    // 输出最短路径数量和最大救援队数量
    printf("%d %d\n", cnt[d], jb[d]);
}

int main(){
    
    
    int n, m;
    elem_t d;

    // 读取输入
    scanf("%d %d %d %d", &n, &m, &s, &d);
    
    // 初始化图和读取城市信息及道路长度
    initialize(n);
    read_store_matrix(n, m);

    // 运行Dijkstra算法
    dijkstra(n, d);

    return 0;
}

5.知識の拡充

ダイクストラのアルゴリズム(Dijkstra)

ダイクストラのアルゴリズム(Dijkstra)は、1 つの頂点から残りの頂点までの最短経路アルゴリズムです。重み付きグラフの最短経路問題を解決します。ダイクストラのアルゴリズムの主な特徴は、始点から開始し、貪欲アルゴリズムの戦略を採用し、終点に至るまで始点に最も近い未訪問の頂点の隣接ノードを毎回トラバースすることです。

Dijkstraこのアルゴリズムは、単一ソースの最短パス問題を解決するための貪欲なアルゴリズムです。このアルゴリズムは、開始点から他のすべての頂点までの最短パスを段階的に見つけます。具体的な手順は次のとおりです。

  1. 初期化: 開始点からそれ自体までの最短パス長を 0 に設定し、他のすべての頂点については最短パス長を無限大に設定します。同時に、最短パスが決定されていない頂点を保存するために、コレクション (通常は優先キューまたは最小ヒープを使用して実装されます) が維持されます。

  2. 頂点の選択: 開始点までの最短パスが既知の最小値となる頂点を選択します。

  3. パスの更新: 選択した頂点について、隣接するすべての頂点をトラバースし、開始点からこれらの隣接する頂点までのパスの長さを更新します。現在選択されている頂点を経由して隣接する頂点へのパスが既知の最短パスより短い場合、最短パスとパスの長さが更新されます。

  4. 決定済みのマークを付ける: 現在選択している頂点を決定済みの最短パスとしてマークし、未決定の最短パスのセットから削除します。

  5. 繰り返し: すべての頂点が決定された最短パスとしてマークされるか、セットが空になるまで、手順 2 ~ 4 を繰り返します。

Dijkstra アルゴリズムの特徴は、非負の重みを持つグラフに対して非常に効果的であることです。各ステップで、現在のパスが最短の頂点が選択され、結果のパスがグローバル最短パスになることが保証されます。ただし、グラフに負の重み付けされたエッジがある場合、 Dijkstra アルゴリズムは誤った結果を生成する可能性があるため、負の重み付けされたエッジを含むグラフの場合、 Bellman-Ford アルゴリズムはより適切な。

ベルマン・フォードアルゴリズム(Bellman-Ford)

ベルマン フォード アルゴリズム(Bellman-Ford) は、単一ソース最短経路問題を解くために使用されるアルゴリズムです。Dijkstra アルゴリズムとは異なります。 a> Bellman-Ford アルゴリズムは負の重みエッジを持つグラフを処理できますが、時間の計算量が高すぎるという欠点があります。基本的な考え方は、エッジの重みを継続的に緩和することによって、最短パスに徐々に近づくことです。具体的な手順は次のとおりです。

  1. 初期化: 開始点からそれ自体までの最短パス長を 0 に設定し、他のすべての頂点については最短パス長を無限大に設定します。同時に、各頂点の前の頂点を記録するために配列が初期化されます。

  2. 緩和エッジ: グラフ内の各エッジについて、現在のパスを通じてより短いパスを取得できるかどうかを継続的に確認します。可能であれば、最短パス長と先行頂点を更新します。

  3. 繰り返し: 頂点のない最短パスの長さが変化するまで、エッジを緩和するプロセスを繰り返します。

  4. 負のウェイト ループの検出: 特定のラウンドの緩和操作で緩和できるエッジがまだある場合、グラフに負のウェイト ループがあることを意味します。負の重みループではパスの長さが継続的に短縮される可能性があるため、最短パスを決定することはできません。

ベルマン フォード アルゴリズムは、負のウェイト エッジを含むグラフに適していますが、時間計算量が O(VE) であるため、V は頂点の数です。 、E はエッジの数です。 アルゴリズムの Dijkstra と比較すると、パフォーマンスは比較的劣ります。グラフがまばらな場合、 アルゴリズムの方が効率的です。 O((V+E)logV)Dijkstra

おすすめ

転載: blog.csdn.net/weixin_40171190/article/details/134745792