- 题目:
- 思路:
该题就是求图的最短路径,首先注意求得是最短路径的数目而不是最短路径的长度,然后还有一个救援队的问题。下面使用两种方法解决该问题:
(1)dijkstra算法:
基本思想:每次找到离源点(如1号节点)最近的一个顶点,然后以该顶点为中心进行扩展,最终得到源点到其余所有点的最短路径。
- 基本步骤:
1,设置标记数组book[]:将所有的顶点分为两部分,已知最短路径的顶点集合P和未知最短路径的顶点集合Q,很显然最开始集合P只有源点一个顶点。book[i]为1表示在集合P中;
2,设置最短路径数组dst[]并不断更新:初始状态下,dst[i]=edge[s]i,很显然此时dst[s]=0,book[s]=1.此时,在集合Q中可选择一个离源点s最近的顶点u加入到P中。并依据以u为新的中心点,对每一条边进行松弛操作(松弛是指由顶点s–>j的途中可以经过点u,并令dst[j]=min(dst[j],dst[u]+edge[u][j])),并令book[u]=1;
3,在集合Q中再次选择一个离源点s最近的顶点v加入到P中。并依据v为新的中心点,对每一条边进行松弛操作(即dst[j]=min(dst[j],dst[v]+edge[v][j])),并令book[v]=1;
4,重复3,直至集合Q为空。 - 代码:
#include <iostream>
#include <cstring>
#define nmax 1000
#define inf 999999
using namespace std;
int minpath[nmax],maxperson[nmax], n, m, edge[nmax][nmax],book[nmax],person[nmax]; //最短路径,节点数,边数,终点,邻接矩阵,集合标记,
int src, dest; //源和目的
int ipath[nmax];
void Dijkstra()
{
//找除了源点外的n-1个点
for (int i = 0; i < n; i++)
{
int min = inf;
int u = 0;
for (int j = 0; j < n; j++)
{
if (book[j] == 0 && minpath[j] < min)
{
min = minpath[j];
u = j;
}
}
book[u] = 1;
//更新最短距离
for (int j = 0; j < n; j++)
{
if (book[j] == 0 && minpath[u] + edge[u][j] < minpath[j])
{
minpath[j] = minpath[u] + edge[u][j];
ipath[j] = ipath[u];
maxperson[j] = maxperson[u] + person[j];
}
else if (book[j] == 0 && minpath[u] + edge[u][j] == minpath[j])
{
ipath[j] += ipath[u];
if (maxperson[j] < maxperson[u] + person[j])
maxperson[j] = maxperson[u] + person[j];
}
}
}
}
int main()
{
int a, b;
int count = 0;
cin >> n >> m >> src >> dest;
for (int i = 0; i < n; i++)
{
cin >> person[i];
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
edge[i][j] = inf;
}
}
for (int i = 0; i < m; i++)
{
cin >> a >> b;
cin >> edge[a][b];
edge[b][a] = edge[a][b];
}
memset(book, 0, sizeof(book));
for (int i = 0; i < n; i++)
{
ipath[i] = 1;
minpath[i] = edge[src][i];
if(minpath[i]!=inf&&i!=src)
maxperson[i] = person[src] + person[i];
}
minpath[src] = 0;
maxperson[src] = person[src];
book[src] = 1;
Dijkstra();
cout << ipath[dest] <<" "<<maxperson[dest];
return 0;
}
(2)深度或广度优先算法:
从起点开始访问所有深度遍历路径或广度优先路径,则到达终点节点的路径有多条,取其中路径权值最短的一条则为最短路径。
和上面dijkstra相比较功能捡漏但是更加容易实现
#include <iostream>
#include <cstring>
#define nmax 1000
#define inf 999999
using namespace std;
int minpath = inf, maxperson = 0, n, m, edge[nmax][nmax], mark[nmax], person[nmax]; //最短路径,节点数,边数,终点,邻接矩阵,节点访问标记
int src, dest; //源和目的
int path = 1;
void dfs(int cur, int dst, int num)
{
if (dst > minpath) return; //如果距离已经大于最小路径了直接返回
if (cur == dest) { //如果到达目的地
if (dst < minpath) {
minpath = dst;
maxperson = num;
path = 1;
}
else if (dst == minpath) {
if(num > maxperson)
maxperson = num;
path += 1;
}
return;
}
for (int i = 0; i < n; i++)
{
if (edge[cur][i] != inf && edge[cur][i] != 0 && mark[i] == 0)
{
mark[i] = 1;
dfs(i, dst + edge[cur][i], num + person[i]);
mark[i] = 0; //递归返回要将mark置0
}
}
}
int main()
{
int a, b;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
edge[i][j] = inf;
}
edge[i][i] = 0;
}
cin >> n >> m >> src >> dest;
for (int i = 0; i < n; i++)
{
cin >> person[i];
}
for (int i = 0; i < m; i++)
{
cin >> a >> b;
cin >> edge[a][b];
edge[b][a] = edge[a][b];
}
memset(mark, 0, sizeof(mark));
mark[src] = 1;
dfs(src, 0, person[src]);
cout << path << " " << maxperson;
return 0;
}