作为一个城市的紧急救援队队长,你得到了一张你的国家的特殊地图。该地图显示了几个由一些道路连接的分散的城市。每个城市的救援队的数量和任何一对城市之间的每条道路的长度都标在地图上。当其他城市向你发出紧急呼叫时,你的工作是带领你的人尽快赶到那个地方,同时在路上尽可能多地召集人手。
输入
每个输入文件包含一个测试案例。对于每个测试案例,第一行包含4个正整数。N(<=500)--城市的数量(而且城市的编号从0到N-1),M--道路的数量,C1和C2--你目前所在的城市和你必须保存的城市,分别。下一行包含N个整数,其中第i个整数是第i个城市中救援队的数量。然后是M行,每行描述一条路,有三个整数c1、c2和L,分别是由一条路连接的一对城市和该路的长度。保证至少存在一条从C1到C2的路径。
输出
对于每个测试案例,在一行中打印两个数字:C1和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
(通过www.DeepL.com/Translator(免费版)翻译)
《原题传送门》https://pintia.cn/problem-sets/994805342720868352/exam/problems/994805523835109376题目大意:从给定的起始点出发到给定终点的所有路中,找到花费时间最少的路线的条数,且找到这些路线中能召集到的最多的人。
分析:先明确城市为点,城市里的人数为点权,城市到城市的路为边,城市到城市所花费时间为边权。我们要做的就是找到边权最小的路线的数目,以及这些路线的点的点权和的最大值。一下提供两种做法:
做法1:对让图以c1为起点进行dfs,等到递归到c2时进行条件判断,从而找到所需要最短路径数量cnt和召集到的最多人手MaxAW。
#include<iostream>
using namespace std;
int n,c1,c2,amg[501][501],visit[501],weight[501],cnt;
int MaxPw, MinAW=99999999;
void dfs(int v,int pw ,int aw) {
visit[v] = 1;
if (v == c2) {
if (aw < MinAW) {
MinAW = aw;
MaxPw = pw;
cnt = 1;
}
else if (aw == MinAW) {
if (pw > MaxPw) {
MaxPw = pw;
}
cnt++;
}
visit[v] = 0;
return;
}
for (int i = 0; i < n; i++) {
if (visit[i] == 0 && amg[v][i] != 0) {
dfs(i, pw + weight[i], aw + amg[v][i]);
}
}
visit[v] = 0;
}
int main() {
int m;
cin >> n >> m >> c1 >> c2;
for (int i = 0; i < n; i++)
cin >> weight[i];
for (int i = 0; i < m; i++) {
int a, b, w;
cin >> a >> b >> w;
amg[a][b] = amg[b][a] = w;
}
dfs(c1, weight[c1], 0);
cout << cnt << " " << MaxPw;
return 0;
}
(以下为对柳神的代码进行讲解)
做法2:dijkstra,建立数组num[ v ]记录c1到v的路线数目,dis[ v ]记录c1到v所需时间,w[ v ]记c1到v所能 召集的人数。每次对边进行松弛时进行条件判断和操作,注意这里的num[v ]数组的操作是由于c1到v的路线不止一条。
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, c1, c2;
int e[510][510], weight[510], dis[510], num[510], w[510];
bool visit[510];
const int inf = 99999999;
int main() {
scanf("%d%d%d%d", &n, &m, &c1, &c2);
for (int i = 0; i < n; i++)
scanf("%d", &weight[i]);
fill(e[0], e[0] + 510 * 510, inf);
fill(dis, dis + 510, inf);
int a, b, c;
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &a, &b, &c);
e[a][b] = e[b][a] = c;
}
dis[c1] = 0;
w[c1] = weight[c1];
num[c1] = 1;
for (int i = 0; i < n; i++) {
int u = -1, minn = inf;
for (int j = 0; j < n; j++) {
if (visit[j] == false && dis[j] < minn) {
u = j;
minn = dis[j];
}
}
if (u == -1) break;
visit[u] = true;
for (int v = 0; v < n; v++) {
if (visit[v] == false && e[u][v] != inf) {
if (dis[u] + e[u][v] < dis[v]) {
dis[v] = dis[u] + e[u][v];
num[v] = num[u];
w[v] = w[u] + weight[v];
}
else if (dis[u] + e[u][v] == dis[v]) {
num[v] = num[v] + num[u];
if (w[u] + weight[v] > w[v])
w[v] = w[u] + weight[v];
}
}
}
}
printf("%d %d", num[c2], w[c2]);
return 0;
}
总结:做法1为暴力做法做法效率不高可以作为做题毫无头绪的一种硬解,建议学一下手听说可以在比赛中吃烂分,但是不能沉迷于此类解法。
做法1:
做法2: