困惑好长时间的一道题,一开始只能拿到14分,过了一年对数据结构的理解更加深入,回来做一做,发现思路很清晰,一遍过。
主要用到Dijksta算法和DFS深度搜索,重点是check函数 。我先找到从起始点到目标点的最短路径是多少,这步由Dijksta函数搞定(注意我们不必完全走完Dijksta 其实只要找到我们想要的最短路径距离返回结果即可,代码中也有注释,同学们可以看代码的同时不理解的读一下注释就豁然明了),然后去找从起始点到目标节点有多少路径,顺便记录路径是什么,用到了vector数组记录,这一步由check函数搞定,大家注意一下剪枝操作,减少代码时间复杂度,不符合条件我就不继续遍历了,剪掉即可。在函数check还调用的cal函数,这个函数主要就是求路径上的救援队数量,很简单,但是为了观看清楚,我就单独写了一个cal函数。
check不仅要记录路径的条数,还要更新最多救援队数量,其次需要记录最优路径。同学们好好理解一下check函数,注释也很详细,可以重点看一看。
到此为止,大概有个理解,然后仔细去看一下代码就明白了,自己写一下,发现就AC了
不清楚的或者wrong的可以评论,我看到就会回复大家的·也可以私信留言
#include<iostream>
#include<vector>
#define INFF 510
using namespace std;
vector<int> jiuyuan;
vector<vector<int>> G;
int sum = 0, mindis = INFF, maxjiu = 0;
int Dijkstra(int n, int s, int d);
void check(vector<int> &vec, int n, int s, int d, int dis);
int cal(vector<int>& vec);
vector<int> result; //保存最优的路径
int flag[INFF] = { 0 };
int main()
{
int n, m, s, d;
cin >> n >> m >> s >> d;
G.resize(n);
jiuyuan.resize(n);
for (int i = 0; i < n; i++)
cin >> jiuyuan[i]; //各个城市救援队数量
for (int i = 0; i < n; i++)
{
G[i].resize(n);
for (int j = 0; j < n; j++)
if (i == j)
G[i][j] = 0;
else
G[i][j] = INFF; //初始化邻接矩阵
}
for (int i = 0; i < m; i++)
{
int a, b, c;
cin >> a >> b >> c;
G[a][b] = G[b][a] = c; //构造化邻接矩阵
}
mindis = Dijkstra(n, s, d); //Dijkstra算法求解从s到d的最短路径(算法不必和标准一样,只要算出从s到d的最短路径即可) 存到mindis中
vector<int> path; //构造路径
path.push_back(s); //先把起始点加入
check(path, n, s, d, 0);
cout << sum << " " << maxjiu << endl;
for (vector<int>::iterator it = result.begin(); it != result.end(); it++)
{
if (it != result.begin()) cout << ' ';
cout << *it;
}
return 0;
}
int Dijkstra(int n, int s, int d)
{
vector<int> ve1(n), flag(n, 0);
flag[s] = 1;
for (int i = 0; i < n; i++)
ve1[i] = G[s][i];
// for(int i=0;i<n-1;i++)
while (1)
{
int m = INFF + 1, j;
for (int k = 0; k < n; k++)
if (!flag[k] && ve1[k] < m)
{
j = k; m = ve1[k];
}
flag[j] = 1;
if (j == d)
return ve1[j]; //只要求出我想要的解 就不必继续往下进行了 return即可
for (int k = 0; k < n; k++)
if (!flag[k] && ve1[k] > ve1[j] + G[j][k])
ve1[k] = ve1[j] + G[j][k]; //更新距离
}
}
void check(vector<int>& vec, int n, int s, int d, int dis)
{
if (dis > mindis || dis == mindis && s != d) //剪枝
return;
if (s == d)
{
sum++; //记录有多少条路径
int t = cal(vec);
if (t > maxjiu) //maxjiu记录最多救援队数量
{
maxjiu = t;
result = vec; //更新
}
}
for (int i = 0; i < n; i++) //递归遍历
{
if (s != i && !flag[i] && G[s][i] != INFF)
{
flag[i] = 1; //标志已经加入vec路径 下次不要重复遍历 否则会出现环 运行超时
vec.push_back(i);
check(vec, n, i, d, dis + G[s][i]); //递归
vec.pop_back(); //注意回溯
flag[i] = 0; //重置标志位
}
}
}
int cal(vector<int>& vec) //计算路径上的救援队数量并返回
{
int t = 0;
for (int i : vec)
t += jiuyuan[i];
return t;
}