LC-1377. T 秒后青蛙的位置(DFS、BFS)

news/2024/7/20 20:47:19 标签: 深度优先, 宽度优先, 算法

1377. T 秒后青蛙的位置

难度困难57

给你一棵由 n 个顶点组成的无向树,顶点编号从 1n。青蛙从 顶点 1 开始起跳。规则如下:

  • 在一秒内,青蛙从它所在的当前顶点跳到另一个 未访问 过的顶点(如果它们直接相连)。
  • 青蛙无法跳回已经访问过的顶点。
  • 如果青蛙可以跳到多个不同顶点,那么它跳到其中任意一个顶点上的机率都相同。
  • 如果青蛙不能跳到任何未访问过的顶点上,那么它每次跳跃都会停留在原地。

无向树的边用数组 edges 描述,其中 edges[i] = [ai, bi] 意味着存在一条直接连通 aibi 两个顶点的边。

返回青蛙在 t 秒后位于目标顶点 target 上的概率。与实际答案相差不超过 10-5 的结果将被视为正确答案。

示例 1:

img

输入:n = 7, edges = [[1,2],[1,3],[1,7],[2,4],[2,6],[3,5]], t = 2, target = 4
输出:0.16666666666666666 
解释:上图显示了青蛙的跳跃路径。青蛙从顶点 1 起跳,第 1 秒 有 1/3 的概率跳到顶点 2 ,然后第 2 秒 有 1/2 的概率跳到顶点 4,因此青蛙在 2 秒后位于顶点 4 的概率是 1/3 * 1/2 = 1/6 = 0.16666666666666666 。 

示例 2:

img

输入:n = 7, edges = [[1,2],[1,3],[1,7],[2,4],[2,6],[3,5]], t = 1, target = 7
输出:0.3333333333333333
解释:上图显示了青蛙的跳跃路径。青蛙从顶点 1 起跳,有 1/3 = 0.3333333333333333 的概率能够 1 秒 后跳到顶点 7 。 

提示:

  • 1 <= n <= 100
  • edges.length == n - 1
  • edges[i].length == 2
  • 1 <= ai, bi <= n
  • 1 <= t <= 50
  • 1 <= target <= n

BFS模拟

BFS模拟,在遍历每个节点时,先查看当前节点的邻接节点是不是都访问过了,如果都访问过了,说明走不动了,原地踏步,将该节点加入到队列中,不然就将没访问过的节点加入到队列中。一共循环t次,最后找队列中值 = target的元素

class Solution {
    public double frogPosition(int n, int[][] edges, int t, int target) {
        if(n == 1 && t == target) return 1.0;
        if(n == 1 && t != target) return 0.0;
        List<Integer>[] g = new ArrayList[n];
        Arrays.setAll(g, e -> new ArrayList<>());
        for(int[] e : edges){
            int x = e[0]-1, y = e[1]-1;
            g[x].add(y);
            g[y].add(x);
        }
        target--;
        Deque<Pair<Integer, Double>> dq = new ArrayDeque<>();
        boolean[] vis = new boolean[n];
        dq.addLast(new Pair<>(0, 1.0));
        vis[0] = true;
        int step = 0;
        while(!dq.isEmpty()){
            int size = dq.size();
            while(size-- > 0){
                Pair<Integer, Double> p = dq.pollFirst();
                int s = g[p.getKey()].size();
                for(int y : g[p.getKey()]){
                    if(vis[y])
                        s -= 1;
                }
                if(s == 0) dq.addLast(p);
                else{
                    for(int y : g[p.getKey()]){
                        if(!vis[y]){
                            vis[y] = true;
                            dq.addLast(new Pair<>(y, p.getValue() * (1.0 / s)));
                        }
                    }
                }
            }
            step += 1;
            if(step == t) break;
        }
        while(!dq.isEmpty()){
            Pair<Integer, Double> p = dq.pollFirst();
            if(p.getKey() == target){
                return p.getValue();
            }
        }
        return 0.0;
    }
}

DFS递归(自顶向下 + 自底向上)

https://leetcode.cn/problems/frog-position-after-t-seconds/solution/dfs-ji-yi-ci-you-qu-de-hack-by-endlessch-jtsr/

既然答案是由若干分子为 1 的分数相乘得到,那么干脆只把分母相乘,最后再计算一下倒数,就可以避免因浮点乘法导致的精度丢失了。另外,整数的计算效率通常比浮点数的高。

  • 自顶向下是一边[递],一边把儿子个数 c 乘起来,如果能在第 t 秒到达 target,或者小于t 秒到达 target 且 target 是叶子节点(此时每次跳跃都会停留在原地) ,那么就记录答案为乘积的倒数,同时返回一个布尔值表示递归结束
  • **自底向上的思路是类似的,找到 target 后,在[归]的过程中做乘法。**个人更喜欢这种写法,因为只在找到 target 之后才做乘法,而自顶向下即使在不含 target 的子树中搜索,也会盲目地做乘法

技巧:
可以把节点 1 添加一个 0 号邻居,从而避免判断当前节点为根节点1,也避免了特判 n = 1的情况

此外,DFS 中的时间不是从 0 开始增加到 t,而是从 leftT = t 开始减小到 0,这样代码中只需和 0 比较,无需和 t 比较,从而减少一个DFS 之外变量的引入。

自顶向下:(递)

class Solution:
    def frogPosition(self, n: int, edges: List[List[int]], t: int, target: int) -> float:
        g = [[] for _ in range(n + 1)]
        g[1] = [0]  # 减少额外判断的小技巧
        for x, y in edges:
            g[x].append(y)
            g[y].append(x)  # 建树
        ans = 0

        def dfs(x: int, fa: int, left_t: int, prod: int) -> True:
            # t 秒后必须在 target(恰好到达,或者 target 是叶子停在原地)
            if x == target and (left_t == 0 or len(g[x]) == 1):
                nonlocal ans 
                ans = 1 / prod
                return True
            if x == target or left_t == 0: return False
            for y in g[x]: # 遍历 x 的儿子 y
                if y != fa and dfs(y, x, left_t-1, prod * (len(g[x]) - 1)):
                    return True # 找到 target 就不再递归了
            return False # 未找到target

        dfs(1, 0, t, 1)
        return ans    

自底向上:(归)

class Solution:
    def frogPosition(self, n: int, edges: List[List[int]], t: int, target: int) -> float:
        g = [[] for _ in range(n + 1)]
        g[1] = [0]  # 减少额外判断的小技巧
        for x, y in edges:
            g[x].append(y)
            g[y].append(x)  # 建树
        ans = 0

        def dfs(x: int, fa: int, left_t: int) -> True:
            # t 秒后必须在 target(恰好到达,或者 target 是叶子停在原地)
            if left_t == 0:
                return x == target
            if x == target:
                return len(g[x]) == 1
            for y in g[x]: # 遍历 x 的儿子 y
                if y != fa: 
                    prod = dfs(y, x, left_t-1) # 寻找 target
                    if prod:
                        return prod * (len(g[x]) - 1)  # 乘上儿子个数,并直接返回
            return 0 # 未找到target

        prod = dfs(1, 0, t)
        return 1 / prod if prod else 0

http://www.niftyadmin.cn/n/354264.html

相关文章

effective c++ 44 与参数无关的代码抽离templates

effective c 44 与参数无关的代码抽离templates 分析 如果模板中有类型无关的参数&#xff0c;那一定得小心&#xff0c;很容易就会出现模板膨胀的问题。 这里有一个矩阵类&#xff0c;并且有一个求逆的方法&#xff0c;这里我们假设这个invert方法有100行代码&#xff0c;为…

怎么提升品牌知名度,小红书母婴赛道分析

小红书平台自创立之初&#xff0c;便以母婴类内容为特色。今天我们来分享下&#xff0c;怎么提升品牌知名度&#xff0c;小红书母婴赛道分析。 一、妈妈用户仍是主流 我们都知道&#xff0c;小红书平台是一个女性用户为主的平台。根据去年的平台用户调查&#xff0c;可以发现&a…

Velocity不用愁!Velocity系统的前端工程化之路 | 京东云技术团队

Velocity是一个基于Java的Web页面模版引擎。十多年前&#xff0c;Velocity将Java代码从Web页面中分离出来&#xff0c;使得开发者能够并行网页开发和Java开发。随着十年前后端分离的浪潮涌动&#xff0c;回首再面对这些基于Velocity的旧系统&#xff0c;无论是后端还是前端人员…

讲解人工智能在现代科技中的应用和未来发展趋势

人工智能是一种模拟人类智能的技术&#xff0c;包括学习、推理、认知、语言等多个方面。在现代科技中&#xff0c;人工智能已经被广泛应用于各种领域&#xff0c;如医疗、金融、交通、安防、教育、农业等。 一、人工智能在大数据现代科技中的应用&#xff1a; 1.医疗领域&…

Android代码中判断so文件是否为64位

/*** 判断so文件是否为64位* param soFile so文件* return so文件为64位返回true&#xff0c;反之返回false*/public boolean isSo64BitAbi(File soFile) {RandomAccessFile randomAccessFile null;try {randomAccessFile new RandomAccessFile(soFile, "r");rando…

【LeetCode】 复制带随机指针的链表

Leetcode 138.复制带随机指针的链表 文章目录 题目描述解题思路运行代码 题目描述 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全…

基于html+css的图展示88

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

嵌入式硬件

嵌入式硬件是一种在电子设备中集成且运行特定程序的硬件。它通常与特定软件应用紧密相关&#xff0c;用于实现一个以上的特定功能&#xff0c;如压缩解压缩、保安服务等。嵌入式系统通常涉及到至少一个控制器&#xff08;或微控制器&#xff09;和其他一些外部芯片&#xff0c;…