1003 Urgence【Pratique PAT (niveau avancé)】
Lien de la question d'origine :Aperçu des détails de la question – 1003 Urgence (pintia.cn)
1.Texte original du titre
En tant que chef d'équipe de secours d'urgence d'une ville, vous recevez une carte spéciale de votre pays. La carte montre plusieurs villes dispersées reliées par quelques routes. Le nombre d'équipes de secours dans chaque ville et la longueur de chaque route entre n'importe quelle paire de villes sont indiqués sur la carte. Lorsqu'un appel d'urgence vous parvient d'une autre ville, votre travail consiste à conduire vos hommes sur place le plus rapidement possible et, en même temps, à appeler autant de personnes que possible en chemin.
Spécification d'entrée :
Chaque fichier d'entrée contient un scénario de test. Pour chaque cas de test, la première ligne contient 4 entiers positifs : N N N ( ≤ 500 \et 500≤500) - le nombre de villes (et les villes sont numérotées de 0 à N − 1 N-1 N−1), M M M - le nombre de routes, C 1 C_1 C1 et C 2 C_2 C2 - les villes dans lesquelles vous vous trouvez actuellement et que vous devez sauvegarder, respectivement. La ligne suivante contient N N N entiers, où i i i-ième entier est le nombre d'équipes de secours dans le i i i-ième ville. Alors M M M lignes suivent, chacune décrit une route avec trois entiers c 1 c_1 c1, c 2 c_2 c2 et L L L, qui sont respectivement la paire de villes reliées par une route et la longueur de cette route. Il est garanti qu'il existe au moins un chemin depuis C 1 C_1 C1 à C 2 C_2 C2.
Spécification de sortie :
Pour chaque scénario de test, imprimez sur une ligne deux nombres : le nombre de chemins les plus courts différents entre C 1 C_1 C1 et C 2 C_2 C2, et le nombre maximum d'équipes de secours que vous pouvez éventuellement rassembler. Tous les nombres d’une ligne doivent être séparés par exactement un espace et aucun espace supplémentaire n’est autorisé à la fin d’une ligne.
Exemple d'entrée :
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
Exemple de sortie :
2 4
2. Traduction du titre
En tant que chef de l'équipe de secours d'urgence d'une ville, vous recevez une carte spéciale de votre pays. La carte montre quelques villes dispersées reliées par quelques routes. La carte indique le nombre d'équipes de secours dans chaque ville et la longueur des routes entre n'importe quelle paire de villes. Lorsque vous recevez un appel d'urgence d'une autre ville, votre travail consiste à conduire votre équipe vers cet endroit le plus rapidement possible tout en appelant autant d'aide que possible en cours de route.
Spécifications d'entrée :
Chaque fichier d'entrée contient un scénario de test. Pour chaque cas de test, la première ligne contient 4 entiers positifs : N N N( ≤ 500 \le 500≤500) - le nombre de villes (numéros de villes de 0 à N − 1 N-1 N−1), M M M - Quantité de route, C 1 C_1 C1somme C 2 C_2 C2 - La ville dans laquelle vous vous trouvez actuellement et la ville que vous devez sauvegarder. La ligne suivante contient N N Nentier, le premier i i iUn entier est le premier i i Nombre d'équipes de secours dans les i villes. Alors M M M lignes, chaque ligne décrit une route et contient trois entiers c 1 c_1 c1, c 2 c_2 c2somme L L L, respectivement, sont les paires de villes reliées par une route et la longueur de la route. Assurez-vous qu'il y en a au moins un de C 1 C_1 C1à C 2 C_2 C2Chemin de.
Spécification de sortie :
Pour chaque scénario de test, imprimez deux nombres sur une seule ligne : C 1 C_1 C1somme C 2 C_2 C2Différent entre le nombre de chemins les plus courts et le nombre maximum d'équipes de secours que vous pouvez éventuellement rassembler. Tous les nombres d'une ligne doivent être séparés par un espace et il ne doit y avoir aucun espace supplémentaire à la fin de la ligne.
Exemple d'entrée :
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
Exemple de sortie :
2 4
3. Idées de résolution de problèmes
3.1 Analyse des questions
Calculez combien de chemins les plus courts il existe pour atteindre la ville cible, et parmi ces chemins, quel est le nombre maximum d'équipes de secours pouvant être convoquées.
Entrez le nombre de villes, le nombre de routes, la ville de départ, la ville cible, le nombre d'équipes de secours dans chaque ville et la longueur des routes entre les villes.
Affichez le nombre de chemins les plus courts et le nombre maximum d'équipes de secours pouvant être convoquées le long de ces chemins.
3.2 Idées de base
Utilisez l'algorithme de Dijkstra pour résoudre le problème du chemin le plus court à source unique, où l'objectif est de trouver le chemin le plus court depuis un point de départ vers tous les autres sommets. Dans ce problème, en plus de trouver le nombre de chemins les plus courts, il est également nécessaire de calculer le nombre maximum d'équipes de secours dans les villes traversées par le chemin le plus court.
3.3 Étapes détaillées
-
Initialisation : Initialise la matrice de contiguïté et le tableau de corrélation du graphique. Définissez le chemin le plus court du point de départ à lui-même sur 0 et définissez la longueur du chemin le plus court sur l'infini pour tous les autres sommets. Dans le même temps, une collection (généralement implémentée à l'aide d'une file d'attente prioritaire ou d'un min-heap) est maintenue pour stocker les sommets pour lesquels le chemin le plus court n'a pas été déterminé.
-
Lire l'entrée : Lire le nombre de villes (n), le nombre de routes (m), le(s) point(s) de départ, le point cible (d) et chaque La relation entre le nombre d'équipes de secours et les liaisons routières dans une ville.
-
Algorithme de Dijkstra : Utilisez l'algorithme de Dijkstra pour calculer le nombre de chemins les plus courts du point de départ au point cible et le nombre maximum d'équipes de secours. À chaque étape, le sommet ayant le chemin actuel le plus court est sélectionné, les longueurs de chemin de ses sommets adjacents sont mises à jour, et le nombre de chemins les plus courts et le nombre maximum d'équipes de secours sont maintenus.
-
Résultats de sortie : Affiche le nombre de chemins les plus courts et le nombre maximum d'équipes de secours.
4. Réponse de référence
#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. Expansion des connaissances
L'algorithme de Dijkstra(Dijkstra)
L'algorithme de Dijkstra(Dijkstra)
est l'algorithme du chemin le plus court d'un sommet aux sommets restants. Il résout le problème du chemin le plus court dans les graphes pondérés. La principale caractéristique de l'algorithme de Dijkstra est qu'il part du point de départ, adopte la stratégie de l'algorithme glouton et parcourt à chaque fois les nœuds adjacents des sommets non visités les plus proches du point de départ jusqu'à ce qu'il s'étende jusqu'au point final.
Dijkstra
L'algorithme est un algorithme glouton pour résoudre le problème du chemin le plus court à source unique. Cet algorithme trouve étape par étape le chemin le plus court depuis le point de départ vers tous les autres sommets. Les étapes spécifiques sont les suivantes :
-
Initialisation : Définit la longueur du chemin le plus court depuis le point de départ jusqu'à lui-même sur 0, et définit la longueur du chemin le plus court sur l'infini pour tous les autres sommets. Dans le même temps, une collection (généralement implémentée à l'aide d'une file d'attente prioritaire ou d'un min-heap) est maintenue pour stocker les sommets pour lesquels le chemin le plus court n'a pas été déterminé.
-
Sélectionner des sommets : Sélectionnez un sommet à partir duquel le chemin le plus court jusqu'au point de départ est le minimum connu.
-
Mettre à jour le chemin : Pour le sommet sélectionné, parcourez tous ses sommets adjacents et mettez à jour la longueur du chemin depuis le point de départ jusqu'à ces sommets adjacents. Si le chemin vers un sommet adjacent via le sommet actuellement sélectionné est plus court que le chemin le plus court connu, le chemin le plus court et la longueur du chemin sont mis à jour.
-
Marquer déterminé : Marquer le sommet actuellement sélectionné comme le chemin le plus court déterminé et le supprimer de l'ensemble des chemins les plus courts indéterminés.
-
Répétez : Répétez les étapes 2 à 4 jusqu'à ce que tous les sommets soient marqués comme chemin le plus court déterminé ou que l'ensemble soit vide.
Dijkstra
La particularité de l'algorithme est qu'il est très efficace pour les graphiques aux poids non négatifs. À chaque étape, il sélectionne le sommet ayant le chemin actuel le plus court, garantissant que le chemin résultant est le chemin le plus court global. Cependant, s'il y a des arêtes à pondération négative dans le graphique, l'algorithme Dijkstra
peut produire des résultats erronés, donc pour les graphiques contenant des arêtes à pondération négative, l'algorithme Bellman-Ford
est plus approprié.
Algorithme de Bellman-Ford(Bellman-Ford)
L'algorithme Bellman-Ford(Bellman-Ford)
est un algorithme utilisé pour résoudre le problème du plus court chemin à source unique. Il est différent de l'algorithme Dijkstra
.Bellman-Ford
peut gérer des graphiques avec des bords de poids négatifs, mais l'inconvénient est que la complexité temporelle est trop élevée. L'idée de base est de s'approcher progressivement du chemin le plus court en relâchant continuellement les poids des bords. Les étapes spécifiques sont les suivantes :
-
Initialisation : Définit la longueur du chemin le plus court depuis le point de départ jusqu'à lui-même sur 0, et définit la longueur du chemin le plus court sur l'infini pour tous les autres sommets. En même temps, un tableau est initialisé pour enregistrer les sommets prédécesseurs de chaque sommet.
-
Arêtes détendues : Pour chaque arête du graphique, essayez continuellement de voir si un chemin plus court peut être obtenu via le chemin actuel. Si possible, mettez à jour la longueur du chemin le plus court et le sommet prédécesseur.
-
Répéter : Répétez le processus d'assouplissement des arêtes jusqu'à ce que la longueur du chemin le plus court sans sommets change.
-
Détecter les boucles de poids négatives : S'il y a encore des bords qui peuvent être détendus lors d'un certain cycle d'opération de relaxation, cela signifie qu'il y a une boucle de poids négatif dans le graphique. Étant donné que les boucles de poids négatif peuvent réduire continuellement la longueur du trajet, le trajet le plus court ne peut pas être déterminé.
L'algorithme de Bellman-Ford convient aux graphiques contenant des arêtes de poids négatif, mais comme sa complexité temporelle est O(VE)
, où V
est le nombre de sommets , < /span> est plus efficace. , les performances sont relativement médiocres. Lorsque le graphique est clairsemé, l'algorithme de l'algorithme E
est le nombre d'arêtes. Par rapport à Dijkstra
O((V+E)logV)
Dijkstra