- 題目:
- 思路:
該題就是求圖的最短路徑,首先注意求得是最短路徑的數目而不是最短路徑的長度,然後還有一個救援隊的問題。下面使用兩種方法解決該問題:
(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;
}