L2-001 紧急救援 (25 分)
试题链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805073643683840
这次自己总结一下Dijkstra算法!(题目在后半部分,看题解的直接跳转后半段吧)
详细总结请看这个我的这篇:
https://blog.csdn.net/qq_36931762/article/details/88806872
Dijkstra算法
这里先大概给大家说一下Dijkstra算法邻接矩阵代码模板:(适合图的点数不超过1000的情况)
代码:
//全局变量部分
const int MAXN = 1000; //图的最大顶点数
const int INF = 1000000000; //设立INF为一个极大数,
//这里这个数也可以设置为0x3f3f3f3f,这代表无穷大,即1061109567这一数值
int n; //当前图的点数
int vis[MAXN]; //标记数组用于记录图中各个点的被访问情况,0为未访问,1为已访问
int dis[MAXN]; //记录起点到各个顶点的最短路径长度
int m[MAXN][MAXN]; //用一个邻接矩阵来记录图中各个点的连接情况
void Dijkstra(int s){ //s为起点
fill(vis, vis+MAXN, 0); //将标记数组初始化为0即未被访问状态,此处可用memset
fill(dis, dis+MAXN, INF); //将最短路数组初始化为一个很大的数,此处要注意不可用memset
dis[s]=0; //首先将起点s到达自身的最短路设为0
for(int i=0;i<n;i++){ //n次循环,遍历完n个点
int node = -1; //node记录当前能找到的从起点到此没被访问的最短的一个点
int minn = INF; //minn记录到达那个点的最短路径
for(int j=0;j<n;j++){ //每个点逐步寻找没被访问的最短的一个点
if(vis[j]==0 && dis[j]<minn){
minn = dis[j];
node = j;
}
}
if(node==-1){ //找不到小于INF的一个点,说明其他结点与定点不连通
return ;
}
vis[node] = 1; //将当前点标记为1
for(int j=0;j<n;j++){
if(vis[j]==0 && m[node][j]!=INF && (dis[node]+m[node][j])<dis[j]){
//如果结点未被访问且 node能到达此结点 并且从起点到此结点过node中介点更近
//在此标尺只有距离,如果有两条距离相等或多条相等,那么就只需在此进行修改
//如路径相同第二标尺为花费时,则可通过在增加花费数组,在此if后加elseif判断距离相等情况花费不同条件。
//下题就有两标尺。
dis[j] = dis[node]+m[node][j];
}
}
}
}
题目:
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出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
大致思路: 思路就是Dijsktra算法思路,然后在其Dijkstra基本算法模板的基础上稍加修改,然后增加了一个限制条件,在利用一点并查里的find思想记录一下当前结点的前一结点从而还原最短路径,由于要求最短路径由起点到终点所以就用一个vector数组来从后往前存放,然后在从后往前遍历输出即形成从起点到终点的路径。
具体代码如下:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int INF=100000000;
int saveteam[505];int st[505];
int mm[505][505], number[505];
int vis[505];
int dis[505];
int father[505];
int n, m, s, d;
void Dijkstra(){
fill(dis, dis+n, INF);
fill(vis, vis+n, 0);
fill(number, number+n, 0);
dis[s] = 0;
st[s] = saveteam[s];
number[s] = 1;
for(int i=0;i<n;i++){
int minn = INF;int node = -1;
for(int j=0;j<n;j++){
if(vis[j]==0 && dis[j]<minn){
minn = dis[j];
node = j;
}
}
vis[node] = 1;
if(node==-1){
return;
}
for(int j=0;j<n;j++){
if(vis[j]==0 && (dis[node] + mm[node][j])<dis[j] && mm[node][j]!=-1){
dis[j] = dis[node] + mm[node][j];
st[j] = st[node] + saveteam[j];
father[j] = node;
number[j] = number[node];
}
else if(vis[j]==0 && (dis[node] + mm[node][j])==dis[j] && mm[node][j]!=-1){ //此处就是判断第二标尺情况,哪条路径上得到救援队最多
if((st[node] + saveteam[j]) > st[j]){
st[j] = st[node] + saveteam[j];
father[j] = node;
}
number[j] += number[node] ;
//这里很关键,不能简单的记录为number[j]++,我一开始就错在这里了。出错样例见后面
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
vector <int> ve;
cin>>n>>m>>s>>d;
for(int i=0;i<n;i++){
cin>>saveteam[i];
}
for(int i=0;i<n;i++){
father[i] = i;
st[i] = saveteam[i];
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
mm[i][j] = -1;
}
}
for(int i=0;i<m;i++){
int n1, n2, value;
cin>>n1>>n2>>value;
mm[n1][n2] = value;
mm[n2][n1] = value;
}
Dijkstra();
int p = d, sum=0;
while(1){
if(p==father[p]){
ve.push_back(p);
break;
}
ve.push_back(p);
p = father[p];
}
cout<<number[d]<<" "<<st[d]<<endl;
for(int i=ve.size()-1;i>=0;i--){
if(i==ve.size()-1){
cout<<ve[i];
}else{
cout<<" "<<ve[i];
}
}
return 0;
}
/* 如果你没有ac这个题可以用以下样例进行测试,测试样例图我会在后面给大家展示一下
样例(1)
6 6 0 3
30 10 20 50 20 20
0 1 1
1 2 2
2 3 3
0 4 1
4 5 2
5 3 3
样例(2)
6 7 0 3
30 100 20 50 20 20
0 1 1
1 3 5
1 2 2
2 3 3
0 4 1
4 5 2
5 3 3
样例(3)
5 5 0 3
30 100 20 50 120
0 1 1
1 2 1
2 3 3
4 3 5
0 4 1
样例(4)
7 8 0 6
10 20 30 50 30 30 20
0 1 1
0 2 1
1 3 2
2 3 2
3 4 3
3 5 3
4 6 4
5 6 4
*/
样例(1)
样例(2)
样例(3)
样例(4)
基本上上面四个样例能过就能过了!!!
之前在天梯赛训练的时候总是觉得自己能力不行做不到这方面的题就一直对这方面的题疏忽练习,然后由于平时也懒得真的把算法模板思考透彻,所以平时就只在算法课上照着模板敲然后解题,但是这次为了下周的天梯赛还是有必要把这些基本算法都彻底搞明白的,也把题目搞明白,所以就一鼓作气总结理解了一下,希望也能对你的学习有帮助,继续坚持!一起加油!