PTA 紧急救援 L2-001(25分)Dijkstra+DFS 最容易理解思路

        困惑好长时间的一道题,一开始只能拿到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;
}
  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值