英雄算法6月27号(迪杰斯特拉)

1514

class Solution {
public:
    double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) {
        vector<vector<pair<double,int>>> graphs(n);//转化为图
        for (int i = 0; i < edges.size(); i++) {//graphs对应下标,存储的是该点所有的儿子(邻居),以及到对应儿子的距离(概率)
            auto& e = edges[i];
            graphs[e[0]].emplace_back(succProb[i], e[1]);
            graphs[e[1]].emplace_back(succProb[i], e[0]);
        }

        queue<pair<double,int>> Queue;
        Queue.push(make_pair(1,start));//存储图的起始位置
        vector<double> distTo(n,0);//distTo,存储start到i的距离(权重)
        distTo[start] = 1;//基础条件
        
        while(!Queue.empty()){
            auto [curProb, curId] = Queue.front();
            Queue.pop();
            for(auto &[probSon, curSon]:graphs[curId]){//遍历儿子
                double distToSon = curProb * probSon; //到son的距离
                if(distToSon > distTo[curSon]){//当前路径下雨原来路径
                    distTo[curSon] = distToSon;//更新
                    Queue.push(make_pair(distTo[curSon],curSon));
                }
            }
        }
        return distTo[end];
    }
};

这题就是缝合怪辣,学习很多大佬的优秀代码

本题使用Dijkstra算法。先介绍该算法是个啥。

  • Dijkstra算法,应用于权重为正数的图中。我们给出起始点,终点,该算法可以返回总权重最大/最小的路径或者权重值(如果每一条路的权重都是1,实际上就是返回最短路径,这玩意儿就退化为BFS
  • Dijkstra算法,就是BFS++,同时运用贪心的思想
    • BFS++
      • 在权重全为1的时候,我们可以将图近似看成多叉树,引入层的概念。每一次遍历我们都往下走一层,最先达到目的地所需要的层数就是最短路径
      • 权重不为1,最先到的不一定是路径最短(引入了权重,虽然经过的节点少,但如果每个节点的权重贼大,哪最终结果也会狠拉,所以不能确定),用层来表示最短路径显然不合适。
      • 为了找出最短路径,我们可以按层遍历,找到所有到达目的地的路径,然后选择最短
    • 贪心
      • 为了保证从start到end的路径最短,我们需要保证从start到其中任意一个节点路径,是可选择路径中最短即可
      • eg:从A到Z,有很多条路径。以任意一条路径为例子,假设该路径编号为1,那么A到1中任意一个节点的路径都是可选路径中最短的,比如A,B都是1路径上的,A到B有很多条路径,但是1号路径是从A到B最短的
    • distTo,记录距离信息的数组
      • 因为层的方式表示距离不合适,我们需要使用新变量存储关于距离的信息,在这里,我们用distTo数组,存储从start到第i个元素的距离

本题解法大致框架如下。题目求解的是最大概率,下文成为最大路径

//将edges转化为graphs,对应下标存储的是下标的所有邻居以及对应概率,方便后续处理
for{
    //双向图,每个节点互为邻居(儿子)
    succProb[i],edges[i][0]->graphs	
    succProb[i],edges[i][1]->graphs
}

//记录从start到i的距离全部赋值为0,因为求最大路径
distTo(n,0)
//start到start的距离是1,这是base case
distTo[start] = 1
//BSF老传统,状态拓展,每个状态存储的是 1.当前所处节点id,2.start到当前所处节点的路径距离
queue<pair<double,int>> Queue= (1,start)

//状态拓展
while(!Queue.empty()){
    curProb,curId = Queue.front()//获取当前要看的节点的信息,curProb:start到当前节点的路径长度,curId:当前节点的Id
    Queue.pop()
      
    //拓展状态,获取当前节点的所有儿子
	for(probSon,curSon->graphs[curId]){
        //找到一条到儿子的路,并计算距离 = 到当前节点的距离*到下一个节点的距离
        distToSon = curProb * probSon 
        //如果新的路径更大,更新,并且拓展状态
        if(distToSon > distTo[curSon]){
            distTo[curSon] = distToSon;//更新
            Queue.push(make_pair(distTo[curSon],curSon));
        }
    }
}
return distTo[end]

LCP 56

class Solution {
    int dir[5] = {-1,0,1,0,-1};//老传统了,配合for(i<4),实现确定4个方向
public:
    int conveyorBelt(vector<string>& matrix, vector<int>& start, vector<int>& end) {
        int m = matrix.size();
        int n = matrix[0].size();
        vector<vector<int>> dp(m,vector<int>(n,INT_MAX));//存储到该点施展魔法次数
        queue<pair<int,int>> Queue;//队列维护状态,每个状态对应一个坐标
        Queue.push(make_pair(start[0],start[1]));
        dp[start[0]][start[1]] = 0;//初始状态

        while(!Queue.empty()){//状态拓展
            auto [R,C] = Queue.front();
            Queue.pop();
            int _nextR = R, _nextC = C;
            getNext(matrix[R][C],_nextR,_nextC);//依据当前节点的字符,获取下一个坐标

            for(int i = 0; i < 4; ++i){//选择4个方向移动
                int nextR = R + dir[i];
                int nextC = C + dir[i+1];
                if(isValid(nextR,nextC,m,n)) continue;//非法
                if(nextR == _nextR && nextC == _nextC && dp[R][C] < dp[nextR][nextC]) {//移动方向和自身符号表示移动方向一致
                    dp[nextR][nextC] = dp[R][C];
                    Queue.push(make_pair(nextR,nextC));
                }
                else if(dp[R][C] + 1 < dp[nextR][nextC]) {//发现一条到达nextR,nextC所需要更少魔法次数的路
                    dp[nextR][nextC] = dp[R][C] + 1;
                    Queue.push(make_pair(nextR,nextC));
                }
            }
        }
        return dp[end[0]][end[1]];
    }

    void getNext(char tar, int& nextR, int& nextC){
        switch(tar){
            case '^':nextR-=1; break;
            case 'v':nextR+=1; break;
            case '<':nextC-=1; break;
            case '>':nextC+=1; break;
        }
    }

    bool isValid(int nextR, int nextC, int m, int n){
        if(nextR < 0 || nextR >= m || nextC < 0 || nextC >= n) return true;
        return false;
    }
};

思路

在每一个节点,枚举四个方向,排除非法情况。并用dp数组维护到达某一节点所需要的最小魔法次数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值