emm,在这里给大家推荐两个方法,一个是我比赛的时候写的堆优化版本的dijkstra,和求最短路径的spfa算法。一般写题的时候都不是接口型的OJ,而leetcode上的oj是接口型的,所以遇到一些些大大小小的问题,一直耽搁了快一个小时,本来就起晚了,AC完这道题也就刚刚结束。
看完排名前面大佬们写的dijkstra用的临界矩阵建图只写了30多行,而我的堆优化版的dijkstra写了70多行,才知道差距,中间有很多不足的地方。
我们这里用两种建图的Dijkstra实现这道题。
注:其实堆优化的Dijkstra就是Dijkstra+优先级队列,可以使得你的时间复杂度优化到O(nlgn),所以 堆优化版的dijkstra = dijkstra + 优先级队列。
如同最小生成树Kruskal 算法就是 并查集 + 排序 + 选择 一样。
题目地址: 这里!
题目描述:
给你一个由 n 个节点(下标从 0 开始)组成的无向加权图,该图由一个描述边的列表组成,其中 edges[i] = [a, b]
表示连接节点 a 和 b 的一条无向边,且该边遍历成功的概率为 succProb[i] 。
指定两个节点分别作为起点 start 和终点 end ,请你找出从起点到终点成功概率最大的路径,并返回其成功概率。
如果不存在从 start 到 end 的路径,请 返回 0 。只要答案与标准答案的误差不超过 1e-5 ,就会被视作正确答案。
输入: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:
输入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.3], start = 0, end = 2
输出:0.30000
输入: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
每两个节点之间最多有一条边
为什么这里要建大根堆,这里再次说明一下吧:
大根堆的堆顶是堆中最大值,小根堆的堆顶是堆中最小值。
在这里我们不是要求点和点之间的最大概率,你第一个节点从优先级队列中出来,是要更新别的点到第一个点的最大概率,如果建小堆,第一个出来的节点会很小,也是要更新其它点的最大概率,建大堆的目的是不是因为有可能建小堆一开始出来的点小,会把最大距离更新的更小。
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做的。
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];
}
};
关于Spfa改日 再补吧。
这次A了三道题 hah,还是菜,加油吧!欠的太多,出来混是要还的啊。