给你一个由 n 个节点(下标从 0 开始)组成的无向加权图,该图由一个描述边的列表组成,其中 edges[i] = [a, b] 表示连接节点 a 和 b 的一条无向边,且该边遍历成功的概率为 succProb[i] 。
指定两个节点分别作为起点 start 和终点 end ,请你找出从起点到终点成功概率最大的路径,并返回其成功概率。
如果不存在从 start 到 end 的路径,请 返回 0 。只要答案与标准答案的误差不超过 1e-5 ,就会被视作正确答案。
示例 1:
输入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.2], start = 0, end = 2
输出:0.25000
示例 2:
输入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.3], start = 0, end = 2
输出:0.30000
示例 3:
输入:n = 3, edges = [[0,1]], succProb = [0.5], start = 0, end = 2
输出:0.00000
提示:
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
每两个节点之间最多有一条边
这道题就是寻找一条从start到end的最长路,那么寻找最长路的方法有很多,这里介绍一下spfa的做法。
首先事根据edges建立双向边,然后从strat到end进行spfa。spfa的一般过程就是遍历图的所有点,并在过程中维护从start到该点的最短或者最大距离。那么这样就需要一个dist数组用来记录距离,由于这里求的是最大者,所以所有dist初始化为0。
接着把start加入队列,dist[start]=0。搜索的过程就很简单了,当队列不为空时,取出对头作为当前的起点,遍历他的所有边计算并维护最大值,如果到目标点的距离比之前大就更新目标点距离并让他入队。
由于这里会多次到达end点,所以我们每次到达end的时候就维护一个ans,每次选择ans和dist[end]的最大值即可。
当搜索结束的时候返回ans。
class Solution {
#define maxn 10005
struct Edge{
int v;
double w;
Edge(){}
Edge(int _v,double _w): v(_v),w(_w){}
};
vector<Edge> e[maxn];
double spfa(int start,int end){
double dist[maxn];
memset(dist,0,sizeof(dist));
queue<int> q;
q.push(start);
dist[start]=1;
double ans=0;
while(!q.empty())
{
int u=q.front();
q.pop();
if(u==end){
ans=max(ans,dist[end]);
}
for(int i=0;i<e[u].size();++i){
int v=e[u][i].v;
double tmp=dist[u]*e[u][i].w;
if(tmp>dist[v]){
dist[v]=tmp;
q.push(v);
}
}
}
return ans;
}
public:
double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) {
for(int i=0;i<edges.size();++i){
int u=edges[i][0];
int v=edges[i][1];
double w=succProb[i];
e[u].push_back(Edge(v,w));
e[v].push_back(Edge(u,w));
}
return spfa(start,end);
}
};
欢迎各位勇者来到力扣城,本次试炼主题为「信物传送」。
本次试炼场地设有若干传送带,matrix[i][j] 表示第 i 行 j 列的传送带运作方向,“^”,“v”,“<”,“>” 这四种符号分别表示 上、下、左、右 四个方向。信物会随传送带的方向移动。勇者每一次施法操作,可临时变更一处传送带的方向,在物品经过后传送带恢复原方向
通关信物初始位于坐标 start处,勇者需要将其移动到坐标 end 处,请返回勇者施法操作的最少次数。
注意:
start 和 end 的格式均为 [i,j]
示例 1:
输入:matrix = [“>>v”,“v^<”,“<><”], start = [0,1], end = [2,0]
输出:1
示例 2:
输入:matrix = [“>>v”,“>>v”,“^<<”], start = [0,0], end = [1,1]
输出:0
示例 3:
输入:matrix = [“>^>“,”<v>”,“v<”], start = [0,0], end = [1,3]
输出:3
提示:
matrix 中仅包含 ‘^’、‘v’、‘<’、‘>’
0 < matrix.length <= 100
0 < matrix[i].length <= 100
0 <= start[0],end[0] < matrix.length
0 <= start[1],end[1] < matrix[i].length
这道题是求最短路,本题的关键在于修改方向该怎么处理,也就是如何维护dist数组。
由于是矩阵,那么自然的可以向上下左右四个方向搜索,既然这样我们就把矩阵的方向映射成数字,来与我们的方向数组的方向对应上,(比如方向数组的0,1,2,3,分别代表着上下左右,那么我们就把矩阵对应的方向映射成他们在方向数组的下标)这样做是为了之后维护dist数组提供方便。同时由于这里是矩阵的spfa,dist数组既可以建立成一维,也可以建立成二维,这里我选择将二维映射成一维,方法也多次提到了行坐标 * 列数 + 列坐标。
当做好准备工作之后,依然是spfa。具体过程为,由于这里求得是最短路,所以dist数组初始化均为0x3f3f3f(多次提到该数字,既便于初始化又足够得大并且支持*2操作)。
具体搜索过程为首先dist[start]=0,start入队,在搜索的过程中我们得到队首 u 的坐标后开始向四个方向搜索,如果越界直接返回,没有越界就选择更新到下一点 v 的距离,这里方向数组下标 i就是我们要前往的方向,如果原状态 u 的方向恰好与 i 相同,那么显然是不需要花费距离的,则有dist[v]=dist[u],而如果方向不同,就需要改变方向到达,这时就有dist[v]=dist[u]+1。这里我们就发现了,到达下一点距离取决于我们搜索的方向和 u 这一点的方向是否相同,相同可以直接到达,否则就改变方向,距离+1。
当距离更新完毕之后,如果小于下一点之前的距离更新并且入队。搜索完毕我们返回到达end的dist即可。
class Solution {
#define maxn 10005
int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
int spfa(int grid[110][110],int m,int n,int start,int end){
int dist[maxn];
memset(dist,0x3f,sizeof(dist));
queue<int> q;
q.push(start);
dist[start]=0;
while(!q.empty()){
int u=q.front();
q.pop();
int x=u/n;
int y=u%n;
for(int i=0;i<4;++i){
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(tx==-1||ty==-1||tx==m||ty==n){
continue;
}
int mask=tx*n+ty;
int tmp=dist[u]+(grid[x][y]==i?0:1);
if(tmp<dist[mask]){
dist[mask]=tmp;
q.push(mask);
}
}
}
return dist[end];
}
public:
int conveyorBelt(vector<string>& matrix, vector<int>& start, vector<int>& end) {
int i,j;
int m=matrix.size();
int n=matrix[0].size();
int grid[110][110];
for(i=0;i<m;++i){
for(j=0;j<n;++j){
if(matrix[i][j]=='^'){
grid[i][j]=0;
}else if(matrix[i][j]=='>'){
grid[i][j]=1;
}else if(matrix[i][j]=='v'){
grid[i][j]=2;
}else if(matrix[i][j]=='<'){
grid[i][j]=3;
}
}
}
return spfa(grid,m,n,start[0]*n+start[1],end[0]*n+end[1]);
}
};
给你一棵由 n 个顶点组成的无向树,顶点编号从 1 到 n。青蛙从 顶点 1 开始起跳。规则如下:
在一秒内,青蛙从它所在的当前顶点跳到另一个 未访问 过的顶点(如果它们直接相连)。
青蛙无法跳回已经访问过的顶点。
如果青蛙可以跳到多个不同顶点,那么它跳到其中任意一个顶点上的机率都相同。
如果青蛙不能跳到任何未访问过的顶点上,那么它每次跳跃都会停留在原地。
无向树的边用数组 edges 描述,其中 edges[i] = [fromi, toi] 意味着存在一条直接连通 fromi 和 toi 两个顶点的边。
返回青蛙在 t 秒后位于目标顶点 target 上的概率。
示例 1:
输入:n = 7, edges = [[1,2],[1,3],[1,7],[2,4],[2,6],[3,5]], t = 2, target = 4
输出:0.16666666666666666 。
示例 2:
输入:n = 7, edges = [[1,2],[1,3],[1,7],[2,4],[2,6],[3,5]], t = 1, target = 7
输出:0.3333333333333333
提示:
1 <= n <= 100
edges.length == n - 1
edges[i].length == 2
1 <= ai, bi <= n
1 <= t <= 50
1 <= target <= n
这一题首先要注意一点,我们搜索的是一个树,再加上题目要求不能返回,所以到达目标点的路径有且只有一个(树无环),那么本题计算所有概率并累加的方法就显然是错的离谱的方法了。
由于树也是图,所以这里我们先建立双向边,然后开始从1到target开始dfs。首先我们要得到该点能够到达的点的数量,由于题目要求不能返回所以要除去父那一条边(这里dfs的参数中要记录每个搜索点的父,既方便了求数量也方便了搜索)。递归终止的条件为step==maxstep( t )。更新答案的条件是当当前搜索的点u == target的时候我们去判断更新答案的两个条件:
(1):该点就是目标点,没有能够再移动的边了并且移动的步数step<=maxstep,这对应着最后一个规则。当step<maxstep的时候我们可以一直在原题跳到最大能到移动的步数,也就意味着不用再往后搜索了,这里直接让step=maxstep,如果恰好等于了maxstep当然更好,我们更新ans。
(2):如果该点是目标点,还能够继续搜索但是步数已经到达了最大步数直接更新ans。(中间搜索到的点不能原地停留)
搜索的过程就是让当前点的每个边走一遍dfs(该点不为父)。那么这样我们的dfs应该拥有的参数为当前点 u ,u的父结点 fa, 到达当前点的距离p (p的更新为 p*1.0/cnt ),当前已经移动的步数step,最大步数maxstep,目标点target。
class Solution {
#define maxn 110
vector<int> e[maxn];
double ans=0;
void dfs(int u,int fa,double p,int step,int maxstep,int target){
int cnt=fa?e[u].size()-1:e[u].size();
if(u==target){
if(!cnt&&step<=maxstep){
ans=p;
step=maxstep;
}else if(cnt&&step==maxstep){
ans=p;
}
}
if(step==maxstep){
return;
}
for(int i=0;i<e[u].size();++i){
if(e[u][i]!=fa){
dfs(e[u][i],u,p*1.0/cnt,step+1,maxstep,target);
}
}
}
public:
double frogPosition(int n, vector<vector<int>>& edges, int t, int target) {
ans=0;
for(int i=0;i<edges.size();++i){
int u=edges[i][0];
int v=edges[i][1];
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0,1,0,t,target);
return ans;
}
};