L2-001 紧急救援(Dijkstra算法 + 记录路径 + 附加其他最优 + 最多种路线)
题目链接: L2-001 紧急救援
题目描述: 作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
样例格式:
输入格式:输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的
个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的
城市编号;D是目的地的城市编号。
第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格
分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速
道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最
优解唯一。
输出格式:第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行
输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余
空格。
输入样例:
4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2
输出样例:
2 60
0 1 3
题解思路:
开始想用dfs来写但是测评后最后一个测评样例超时,所有只能考虑用Dijkstra算法,但是这道题不仅要处理最短路径,还要记录最大的救援队数量,和最短路径数量,同时还要记录路径。
想想最短路算法的思想,就是想象一下多米诺骨牌的情况,边长就是每个转折点之间的牌数,然后看那条路径最早击倒终点牌,或者想象一个“绳索计算机”, 每一个结点用绳子串起来,然后放在竖直桌面,拉住起始点,然后向上拉,记录每一个先出桌面的点。
这题就是要在每一次找到最短距离的那个点的同时记录相关信息,就可以,具体看代码。
代码如下:
超时dfs代码(错误代码)
#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define pf printf
#define ll long long
#define pb push_back
#define endl '\n'
#define sz size
#define INF 0x3f3f3f3f
#define max(a, b) a > b ? a : b
#define min(a, b) a < b ? a : b
const int N = 505;
int n, m, s, d;
int jyd[N];
int len[N][N];
vector<int> cd[N];
int vis[N];
vector<int> path;
vector<int> tpath;
int sumpath = 0;
int mjyd = 0, tjyd = 0;
int mlen = INF, tlen = 0;
int flag = 0;
void dfs(int ss)
{
if(flag == 1){
flag = 0;
return;
}
if(ss == d){
if(tlen < mlen){
sumpath = 1;
mjyd = tjyd;
mlen = tlen;
path.assign(tpath.begin(), tpath.end());
}
else if(tlen > mlen){
flag = 1;
return;
}
else{
sumpath++;
if(tjyd > mjyd){
mjyd = tjyd;
path.assign(tpath.begin(), tpath.end());
}
}
}
else{
for(int i = 0; i < cd[ss].sz(); i++){
int nss = cd[ss][i];
if(vis[nss] == 0){
tjyd += jyd[nss];
tlen += len[ss][nss];
tpath.pb(nss);
vis[nss] = 1;
dfs(nss);
tjyd -= jyd[nss];
tlen -= len[ss][nss];
tpath.pop_back();
vis[nss] = 0;
}
}
}
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> m >> s >> d;
for(int i = 0; i < n; i++) cin >> jyd[i];
int aa, bb, cc;
for(int i = 0; i < m; i++){
cin >> aa >> bb >> cc;
len[aa][bb] = len[bb][aa] = cc;
cd[aa].pb(bb); cd[bb].pb(aa);
}
memset(vis, 0, sizeof(vis));
vis[s] = 1;
dfs(s);
cout << sumpath << " " << mjyd + jyd[s] << endl;
cout << s << " ";
for(int i = 0; i < path.sz(); i++){
if(i != path.sz() - 1) cout << path[i] << " ";
else cout << path[i] << endl;
}
return 0;
}
AC代码: (Dijkstra算法)
#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define pf printf
#define ll long long
#define pb push_back
#define endl '\n'
#define sz size
#define INF 0x3f3f3f3f
#define max(a, b) a > b ? a : b
#define min(a, b) a < b ? a : b
const int N = 505;
int n, m, s, d;
int jyd[N];
int len[N][N];
int num[N];
int mjyd[N];
int pre[N];
int dist[N];
int st[N];
void dijkstra()
{
memset(st, 0, sizeof(st));
memset(dist, 0x3f, sizeof(dist));
dist[s] = 0;
memset(num, 0, sizeof(num));
num[s] = 1;
memset(mjyd, 0, sizeof(mjyd));
mjyd[s] = jyd[s];
for(int i = 0; i < n - 1; i++){
int t = -1;
for(int j = 0; j < n; j++){
if(st[j] == 0 && (t == -1 || dist[t] > dist[j])){
t = j;
}
}
for(int j = 0; j < n; j++){
if(dist[t] + len[t][j] < dist[j]){
dist[j] = dist[t] + len[t][j];
num[j] = num[t];
mjyd[j] = mjyd[t] + jyd[j];
pre[j] = t;
}
else if(dist[t] + len[t][j] == dist[j]){
num[j] += num[t];
if(mjyd[t] + jyd[j] > mjyd[j]){
mjyd[j] = mjyd[t] + jyd[j];
pre[j] = t;
}
}
}
st[t] = 1;
}
}
void print(int ss, int tt)
{
if(ss == tt){
cout << ss << " ";
return;
}
print(ss, pre[tt]);
cout << tt << " ";
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> m >> s >> d;
for(int i = 0; i < n; i++) cin >> jyd[i];
memset(len, 0x3f, sizeof(len));
int aa, bb, cc;
for(int i = 0; i < m; i++){
cin >> aa >> bb >> cc;
len[aa][bb] = len[bb][aa] = cc;
}
dijkstra();
cout << num[d] << " " << mjyd[d] << endl;
print(s, pre[d]);
cout << d << endl;
return 0;
}