leetcode第197场周赛 之 5211概率的最大路径

emm,在这里给大家推荐两个方法,一个是我比赛的时候写的堆优化版本的dijkstra,和求最短路径的spfa算法。一般写题的时候都不是接口型的OJ,而leetcode上的oj是接口型的,所以遇到一些些大大小小的问题,一直耽搁了快一个小时,本来就起晚了,AC完这道题也就刚刚结束。
看完排名前面大佬们写的dijkstra用的临界矩阵建图只写了30多行,而我的堆优化版的dijkstra写了70多行,才知道差距,中间有很多不足的地方。
我们这里用两种建图的Dijkstra实现这道题。

注:其实堆优化的Dijkstra就是Dijkstra+优先级队列,可以使得你的时间复杂度优化到O(nlgn),所以 堆优化版的dijkstra = dijkstra + 优先级队列
如同最小生成树Kruskal 算法就是 并查集 + 排序 + 选择 一样。

leetcode周赛第3题 概率的最大路径

题目地址: 这里!

题目描述

给你一个由 n 个节点(下标从 0 开始)组成的无向加权图,该图由一个描述边的列表组成,其中 edges[i] = [a, b] 
表示连接节点 a 和 b 的一条无向边,且该边遍历成功的概率为 succProb[i] 。
指定两个节点分别作为起点 start 和终点 end ,请你找出从起点到终点成功概率最大的路径,并返回其成功概率。
如果不存在从 start 到 end 的路径,请 返回 0 。只要答案与标准答案的误差不超过 1e-5 ,就会被视作正确答案。

示例 1:
leetcode第197场周赛 之 5211概率的最大路径_第1张图片

输入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.2], start = 0, end = 2
输出:0.25000
解释:从起点到终点有两条路径,其中一条的成功概率为 0.2 ,而另一条为 0.5 * 0.5 = 0.25

示例 2:

leetcode第197场周赛 之 5211概率的最大路径_第2张图片

输入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.3], start = 0, end = 2
输出:0.30000

示例 3:
leetcode第197场周赛 之 5211概率的最大路径_第3张图片

输入:n = 3, edges = [[0,1]], succProb = [0.5], start = 0, end = 2
输出:0.00000
解释:节点 0 和 节点 2 之间不存在路径
提示:

2 <= n <= 10^4
0 <= start, end < n
start != end
0 <= a, b < n
a != b
0 <= succProb.length == edges.length <= 2*10^4
0 <= succProb[i] <= 1
每两个节点之间最多有一条边

算法思想:

  1. 拨开题目要求,探讨这道题的本质,确定是最短路径的相关题目。确定大致算法,Dijkstra算法,Floyed算法,spfa算法,堆优化的Dijkstra算法,然后看这道题的数据范围n = 10^4 ,好,我们排除Floyed算法(O(n3)),然后感觉一下,O(n2)有可能也会超时,我们将Floyed也先排除,实际上真的超时了。所以现在我们的算法只有 堆优化的Dijkstra算法,和Spfa算法。但是Spfa的时间复杂度为O(nm), m是边数,这里的边数是2*10^4有可能也难顶。(但是我最后试了试发现可以过)。
  2. 然后就是一般的建图流程,我采用的是邻接表建图。因为是无向边,所以要建双向的路径。
  3. 注意这里的优先级队列是大根堆的,每一次从队列中去更新其它的边的概率要选择概率最大的去更新其点的概率。
  4. 起始点的概率初始化为1.0,然后通过大根堆去不断的更新其它点的最大概率即可。
  5. 题目中坑的地方,一开始我不知道这里的n要取多大,所以就全部按照const int N = 1e4 + 20,来算,实际上这里的n已经告诉你了,第一个参数n的含义就是 城市的多少,我把它看成了 edges的大小(路径的条数了),实际上不是,所以一直报一些堆溢出的错误,难改的一批,调试了半天,直到自己试了一些条件的测试用例,才发现错误。实际上不需要定义那些的N,可以完全使用参数中给的n。

为什么这里要建大根堆,这里再次说明一下吧:
大根堆的堆顶是堆中最大值,小根堆的堆顶是堆中最小值。
在这里我们不是要求点和点之间的最大概率,你第一个节点从优先级队列中出来,是要更新别的点到第一个点的最大概率,如果建小堆,第一个出来的节点会很小,也是要更新其它点的最大概率,建大堆的目的是不是因为有可能建小堆一开始出来的点小,会把最大距离更新的更小。

堆优化版本Dijkstra的代码实现:

const int N = 1e4 + 20;
const int M = 1e5;
class Solution {
public:

	double dis[N];
	int h[N], ne[M], e[M];
	int idx = 0;
	double w[M];
	bool st[N];
	typedef pair<double, int> PII;
	void add(int a, int b, double c)
	{
		e[idx] = b;
		ne[idx] = h[a];
		w[idx] = c;
		h[a] = idx++;
	}
	void dijkstra(int start, int end)
	{
		memset(dis, 0, sizeof dis);
		memset(st,false,sizeof st);
		priority_queue<PII, vector<PII>, less<PII>> pq; //大根堆的
		dis[start] = 1.0;
		pq.push({ dis[start], start });
		while (pq.size())
		{
			auto tmp = pq.top();
			pq.pop();
			int ver = tmp.second;
			double distance = tmp.first;
			if (st[ver]) continue;
			st[ver] = true;
			for (int i = h[ver]; i != -1; i = ne[i])
			{
				int j = e[i];
				if (dis[j] < dis[ver] * w[i])
				{
					dis[j] = dis[ver] * w[i];
					pq.push({ dis[j], j });
				}
			}
		}
	}
	double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) {

		// double g[N][N];
		memset(h, -1, sizeof h);
		memset(ne, 0, sizeof ne);
		memset(e, 0, sizeof e);

		for (int i = 0; i < edges.size(); i++)
		{
			int a = edges[i][0];
			int b = edges[i][1];
			add(a, b, succProb[i]);
			add(b, a, succProb[i]);
		}
		dijkstra(start, end);
		return dis[end];


	}
};

有可能这里没有算考试的时候的人做的,而我这种方法提交的在前面的原因吧。当然还有一群大佬们是有bfs做的。
leetcode第197场周赛 之 5211概率的最大路径_第4张图片

堆优化版本Dijkstra邻接矩阵的代码实现:


const double eps = 1e-8;
class Solution {
public:
    double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) {
        vector<double> dis(n);
        dis[start] = 1;
        vector<vector<pair<int,double>>> vvp(n);
        for(int i = 0; i < edges.size(); i++)
        {
            int u = edges[i][0];
            int v = edges[i][1];
            double p = succProb[i];
            vvp[u].push_back({v,p});
            vvp[v].push_back({u,p});
        }
        priority_queue<pair<double,int>> pq;
        pq.push({dis[start],start});
        vector<bool> vis(n);
        while(pq.size())
        {
            auto tmp = pq.top();
            int u = tmp.second;
            double p = tmp.first;
            pq.pop();
            if(vis[u])
                continue;
            vis[u] = true;
            if(p < eps)
                continue;
            for(auto edge:vvp[u])
            {
                int v = edge.first;
                double now  = p * edge.second;
                if(dis[v] < now)
                {
                    dis[v] = now;
                    pq.push({dis[v],v});
                }
            }
        }
        return dis[end];
    }
};

leetcode第197场周赛 之 5211概率的最大路径_第5张图片

关于Spfa改日 再补吧。

这次A了三道题 hah,还是菜,加油吧!欠的太多,出来混是要还的啊。

你可能感兴趣的:(C++,算法)