一、题目
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出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算法。dijkstra算法用来求单源点最短路径,因此可以求出从起点到终点的最短距离,但是这个题远远不止求出距离这么简单,还要求最短路径的条数,记录最短路径上的点,求不同最短路径的救援队数量最大值:
- 求最短路径的条数的思路:使用数组 dij[] 来保存从s出发到其他各个点的距离,path[] 表示相同距离的情况下,从前一个点到该点的路径条数,初始为0,更新距离时,若dij[i]的值不变,则path[i]++,否则,path[i]=1,说明当前距离还不是最短的。
- 记录最短路径上的点的思路:用一个数组 pre[] 来记录前一个点,pre[1]=2,表示1的前一个点是2,由此可以向前推,直至源点s。
- 如何确定pre[i]的值:用一个num[]数组来表示到某点的最大救援队数量,如果2和3就能到达4,且距离相同,则pre[4]的值为num值更大的那一个点。
三、代码
#include <bits/stdc++.h>
using namespace std;
const int inf=0x3fffffff;
int main()
{
int n,s,d;
long long int m;
cin>>n>>m>>s>>d;
long long int a[n];
for(int i=0;i<n;i++) cin>>a[i];
long long int gra[n][n];
long long int dij[n];
long long int path[n];
long long int num[n];
int pre[n];
int vis[n];
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++) gra[i][j]=inf;
}
for(int i=0;i<m;i++)
{
int u,v;
long long int w;
cin>>u>>v>>w;
gra[u][v]=gra[v][u]=w;
}
for(int i=0;i<n;i++)
{
dij[i]=gra[i][s];
vis[i]=0;
if(gra[i][s]!=inf)
{
path[i]=1;
pre[i]=s;
num[i]=a[s]+a[i];
}
else
{
path[i]=0;
pre[i]=-1;
}
}
dij[s]=0;
vis[s]=1;
num[s]=a[s];
for(int i=0;i<n;i++)
{
int min1=inf;
int mini=-1;
for(int j=0;j<n;j++)
{
if(vis[j]==0)
{
if(dij[j]<min1)
{
min1=dij[j];
mini=j;
}
}
}
vis[mini]=1;
if(mini==d) break;
for(int j=0;j<n;j++)
{
if(vis[j]==0)
{
if(dij[j]>dij[mini]+gra[mini][j])
{
dij[j]=dij[mini]+gra[mini][j];
path[j]=1;
pre[j]=mini;
num[j]=a[j]+num[mini];
}
else if(dij[j]==dij[mini]+gra[mini][j])
{
path[j]++;
if(num[j]<num[mini]+a[j])
{
num[j]=num[mini]+a[j];
pre[j]=mini;
}
}
}
}
}
int d1=d;
vector<int> v;
int nu=path[d];
while(pre[d1]!=s)
{
d1=pre[d1];
if(d1!=s)
{
nu=nu*path[d1];
v.push_back(d1);
}
}
cout<<nu<<" "<<num[d]<<endl;
cout<<s;
for(int i=v.size()-1;i>=0;i--)
{
cout<<" "<<v[i];
}
cout<<" "<<d<<endl;
return 0;
}
花了好长时间自己写的代码,得了18分,求指点!!!
四、知识点总结
4.1 无穷大常量
- 0x7fffffff
在十进制表示下,0x7fffffff = 2147483647,恰好等于int型变量的上界。 - 0x3fffffff
0x3fffffff = 1073741823 > 1e9,这个数不仅满足了比程序中的任何数都大,而且0x3fffffff + 0x3fffffff = (1 << 31) - 2小于int型的边界(1 << 31) - 1 = 2147483647,因此满足了无穷大加上一个数还是无穷大的条件。 - 0x3f3f3f3f
0x3f3f3f3f = 1061109567 > 1e9,0x3f3f3f3f + 0x3f3f3f3f = 2122219134 < int型的边界(1 << 31) - 1 = 2147483647,因此也满足无穷大加上一个无穷大还是无穷大的条件。
#define INF1 0x7fffffff
#define INE2 0x3fffffff
#define INF3 0x3f3f3f3f
const int INF4 = 0x7fffffff;
const int INF5 = 0x3fffffff;
const int INF6 = 0x3f3f3f3f;
4.2 迪杰斯特拉算法(Dijkstra)
- Dijkstra算法是用于解决单源最短路径问题,可以有效解决无环图的图论问题,时间复杂度最坏为O(n^2)。
- 此算法无法解决权值为负的相关问题。
- 当题目中出现“从某一个城市出发到达另一个目的地的最短路程”相类似的句子,同时它的路径都是正值时,可以考虑使用Dijkstra算法。
- Dijkstra算法的基本框架:
a.使用邻接矩阵(二维数组) gra[][] 来保存图,
b.数组 dij[] 来保存从当前点出发到其他各个点的距离
c.数组 v[] 来表示当前点是否被访问过 - 此算法最关键的点在于:选择的dij值最小的点一定是不需要被更新的点,以它为出发点更新dij数组一定是最优的。
- 算法伪代码:
dij[a] = 0,其他dij[i]=INF//INF为定义的最大值
循环n次:
在所以未被标记的点中选择出dij值最小的 记为x
标记x
对于所有以x开头的边,dij[i] = min(dij[i] , dij[x]+gra[x][i])
参考博客:
无穷大常量0x7fffffff 0x3fffffff 0x3f3f3f3f
dijkstra算法讲解(例题:L2-001 紧急救援 (25 分))