LC-6260. 矩阵查询可获得的最大分数(最小堆,并查集+离线(海平面上升问题))【周赛323】

news2024/12/23 8:45:01

6260. 矩阵查询可获得的最大分数

难度困难7

给你一个大小为 m x n 的整数矩阵 grid 和一个大小为 k 的数组 queries

找出一个大小为 k 的数组 answer ,且满足对于每个整数 queres[i] ,你从矩阵 左上角 单元格开始,重复以下过程:

  • 如果 queries[i] 严格 大于你当前所处位置单元格,如果该单元格是第一次访问,则获得 1 分,并且你可以移动到所有 4 个方向(上、下、左、右)上任一 相邻 单元格。
  • 否则,你不能获得任何分,并且结束这一过程。

在过程结束后,answer[i] 是你可以获得的最大分数。注意,对于每个查询,你可以访问同一个单元格 多次

返回结果数组 answer

示例 1:

img

输入:grid = [[1,2,3],[2,5,7],[3,5,1]], queries = [5,6,2]
输出:[5,8,1]
解释:上图展示了每个查询中访问并获得分数的单元格。

示例 2:

img

输入:grid = [[5,2,1],[1,1,2]], queries = [3]
输出:[0]
解释:无法获得分数,因为左上角单元格的值大于等于 3 。

提示:

  • m == grid.length
  • n == grid[i].length
  • 2 <= m, n <= 1000
  • 4 <= m * n <= 105
  • k == queries.length
  • 1 <= k <= 104
  • 1 <= grid[i][j], queries[i] <= 106

暴力模拟(超时)

class Solution {
    int[][] dirt = {{0,1},{0,-1},{1,0},{-1,0}};
    boolean[][] visited;
    int n,m;
    public int[] maxPoints(int[][] grid, int[] queries) {
        n = grid.length;
        m = grid[0].length;
        int[] res = new int[queries.length];
        for(int i = 0; i < res.length; i++){
            if(grid[0][0] > queries[i]) continue;
            visited = new boolean[n][m];
            visited[0][0] = true;
            res[i] = dfs(0,0,queries[i],grid);
        }
        return res;
    }
    
    public int dfs(int i, int j, int max, int[][] grid){
        if(grid[i][j] >= max) return 0;
        int res = 1;
        for(int k = 0; k < dirt.length; k++){
            int newx = i + dirt[k][0],newy = j + dirt[k][1];
            if(newx >= 0 && newx < n && newy >= 0 && newy < m && !visited[newx][newy]){
                visited[newx][newy] = true;
                res += dfs(newx,newy,max,grid);
            }
        }
        return res;
    }
}

转化为海平面上升问题

https://leetcode.cn/problems/maximum-number-of-points-from-grid-queries/solution/by-liuliangcan-9osu/

  • 题目的询问query实际上limit,即询问:对于limit从左上角开始,能floodfill到的格子个数。
  • 再转换一下:左上角从虚空连接了高度limit的海平面,问能淹几个格子。
  • 那么把询问离线排序,从小到大处理即可。
  • 队列用小顶堆存,含义为:边界的格子最矮值先被淹。
  • 于是只有堆顶小于limit时才需要继续搜索;否则等待海平面上升。

题解来自:0x3f : https://leetcode.cn/problems/maximum-number-of-points-from-grid-queries/solution/by-endlesscheng-qeei/

方法一:并查集

一看到这种具有大小关系的查询,直接就是经典的并查集加离线算法套路,力扣上有很多类似的题目,周赛双周赛就出现了好多次这样的!

把矩阵的元素值从小到大排序,询问也从小到大排序。

用双指针遍历矩阵元素值和询问,如果矩阵元素值小于询问值,就把该格子和周围四个格子中的小于询问值的格子相连。

用并查集可以实现相连的过程,同时维护每个连通块的大小。

答案就是左上角的连通块的大小(前提是左上角小于询问值)。

如果每次询问一次,dfs 一次,很可能超时,因此可以用并查集。离线处理,把边按权值排序,把问题按大小排序。然后离线的过程就是不断向图中加边的过程。

# 网格图
# 边是什么?边权是什么?
# 抽象成图
# 答案就是包含左上角的连通块的大小
# 连通块需要满足,点权 < q
# 点权处理起来比较麻烦,转换成边权就比较方便了,可以直接用并查集合并

# 怎么转换成边权?
# 把两个点连起来,需要满足什么条件?
#   - 两个点的点权都是 < q
# 边权 = 两个点的点权的最大值

# 边权排序 询问排序
# 双指针遍历 边权 和 询问
# 每次把 < q 的边 所对应的两个点合并起来
# 答案 就是左上角(0)的连通块大小

python

class Solution:
    def maxPoints(self, grid: List[List[int]], queries: List[int]) -> List[int]:
        # 方法一:并查集
        m,n = len(grid), len(grid[0])
        mn = m*n

        # 并查集模板
        fa = list(range(mn))
        size = [1] * mn # 自成一个大小为1的连通块
        def find(x: int) -> int:
            if fa[x] != x:
                fa[x] = find(fa[x])
            return fa[x]
        def merge(f: int, to: int) -> None:
            f = find(f)
            to = find(to)
            if f != to:
                fa[f] = to
                size[to] += size[f]
                
        # 根据邻居关系进行建图处理
        edges = []
        for i, row in enumerate(grid):
            for j,x in enumerate(row): # 对每个点只与其上面和左边建立边
                if i > 0: # 建边(边权,当前点,上/左点)
                    edges.append((max(x,grid[i-1][j]),i*n+j,(i-1)*n+j)) # 当前点与上面点连边 
                if j > 0: 
                    edges.append((max(x,grid[i][j-1]),i*n+j,i*n+j-1)) # 当前点与左边点连边
        edges.sort(key=lambda p:p[0]) # 根据边权排序

        ans = [0]*len(queries)
        j = 0
        # 按照查询值的大小排序,依次进行查询
        for i,q in sorted(enumerate(queries), key = lambda p:p[1]): # 将queries值和下标合起来 根据值排序
            # 根据查询值的大小利用指针持续更新并查集
            while j < len(edges) and edges[j][0] < q:
                merge(edges[j][1],edges[j][2])
                j += 1
            if grid[0][0] < q:
                ans[i] = size[find(0)]
        return ans

java

class Solution {
    private static final int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    private int[] fa, size;

    public int[] maxPoints(int[][] grid, int[] queries) {
        int m = grid.length, n = grid[0].length, mn = m * n;

        // 并查集
        fa = new int[mn];
        for (var i = 0; i < mn; i++) fa[i] = i;
        size = new int[mn];
        Arrays.fill(size, 1);

        // 矩阵元素从小到大排序,方便离线
        var a = new int[mn][3];
        for (var i = 0; i < m; ++i)
            for (var j = 0; j < n; ++j)
                a[i * n + j] = new int[]{grid[i][j], i, j};
        Arrays.sort(a, (p, q) -> p[0] - q[0]);

        // 查询的下标按照查询值从小到大排序,方便离线
        var k = queries.length;
        var id = IntStream.range(0, k).boxed().toArray(Integer[]::new);
        Arrays.sort(id, (i, j) -> queries[i] - queries[j]);

        var ans = new int[k];
        var j = 0;
        for (var i : id) {
            var q = queries[i];
            for (; j < mn && a[j][0] < q; ++j) {
                int x = a[j][1], y = a[j][2];
                // 枚举周围四个格子,值小于 q 才可以合并
                for (var d : dirs) {
                    int x2 = x + d[0], y2 = y + d[1];
                    if (0 <= x2 && x2 < m && 0 <= y2 && y2 < n && grid[x2][y2] < q)
                        merge(x * n + y, x2 * n + y2); // 把坐标压缩成一维的编号
                }
            }
            if (grid[0][0] < q)
                ans[i] = size[find(0)]; // 左上角的连通块的大小
        }
        return ans;
    }

    // 并查集模板
    private int find(int x) {
        if (fa[x] != x) fa[x] = find(fa[x]);
        return fa[x];
    }

    private void merge(int from, int to) {
        from = find(from);
        to = find(to);
        if (from != to) {
            fa[from] = to;
            size[to] += size[from];
        }
    }
}

方法二:最小堆

仍然是离线询问,还可以从左上角出发向外搜索,用最小堆,初始把左上角的元素值及其坐标入堆。对每个询问,不断循环,如果堆顶元素值小于询问值,则弹出堆顶,继续搜索。

循环结束时,出堆的元素个数就是答案。

# 初始,把左上角和坐标(grid[0][0], 0, 0) 放到一个最小堆中
# 遍历排序后的询问,再套一个while循环
# 不断取堆顶,直到堆为空,或者 堆顶 >= q
#   循环内部:把这个位置向四种拓展,把拓展后的格子的值和坐标加到堆中
#   统计循环次数,或者 <q  的值的个数 cnt
# 循环结束之后,cnt 就是答案

python

class Solution:
    def maxPoints(self, grid: List[List[int]], queries: List[int]) -> List[int]:
        m,n = len(grid), len(grid[0])
        ans = [0]*len(queries)
        h = [(grid[0][0], 0, 0)] # 将左上角放到堆中
        grid[0][0] = 0 # 表示访问过了
        cnt = 0
        # 按照查询值的大小排序,依次进行查询
        for qi,q in sorted(enumerate(queries), key = lambda p:p[1]):
            # 不断取堆顶,直到堆为空,或者 堆顶 >= q
            while h and h[0][0] < q:
                cnt += 1
                val, i, j = heappop(h)
                # 遍历结点四周
                for x, y in (i+1,j),(i-1,j),(i,j+1),(i,j-1):
                    if 0<=x<m and 0<=y<n and grid[x][y]: # 合法且未访问
                        heappush(h, (grid[x][y], x, y))
                        grid[x][y] = 0
            ans[qi] = cnt
        return ans

java

class Solution {
    private static final int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

    public int[] maxPoints(int[][] grid, int[] queries) {
        // 查询的下标按照查询值从小到大排序,方便离线
        var k = queries.length;
        var id = IntStream.range(0, k).boxed().toArray(Integer[]::new);
        Arrays.sort(id, (i, j) -> queries[i] - queries[j]);

        var ans = new int[k];
        var pq = new PriorityQueue<int[]>((a, b) -> a[0] - b[0]);
        pq.add(new int[]{grid[0][0], 0, 0});
        grid[0][0] = 0; // 充当 vis 数组的作用
        int m = grid.length, n = grid[0].length, cnt = 0;
        for (var i : id) {
            var q = queries[i];
            while (!pq.isEmpty() && pq.peek()[0] < q) {
                ++cnt;
                var p = pq.poll();
                for (var d : dirs) { // 枚举周围四个格子
                    int x = p[1] + d[0], y = p[2] + d[1];
                    if (0 <= x && x < m && 0 <= y && y < n && grid[x][y] > 0) {
                        pq.add(new int[]{grid[x][y], x, y});
                        grid[x][y] = 0; // 充当 vis 数组的作用
                    }
                }
            }
            ans[i] = cnt;
        }
        return ans;
    }
}

相关题目

1697. 检查边长度限制的路径是否存在

在线算法:

  • 在线算法就是边输入边处理,不一定要等所有输入都完成以后才会给出相应的解答。

离线算法:

  • 在问题的一开始就要知道所有的数据,然后才能输出结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/80877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022年了,你还没搞清楚箭头函数与普通函数的区别嘛?

目录 1.箭头函数简介 2.箭头函数与普通函数的区别 A.声明方式不同&#xff0c;匿名函数 B.this指向不同 C.箭头函数的this永远不会变&#xff0c;call、apply、bind也无法改变 D.箭头函数没有原型prototype E.箭头函数不能当成一个构造函数 F.箭头函数没有自己的argume…

javaSE(数据类型、运算、逻辑控制、方法)

1.初识Java JDK、JRE、JVM之间的关系&#xff1f; JDK(Java Development Kit):Java开发工具包&#xff0c;提供给Java程序员使用&#xff0c;包含了JRE&#xff0c;同时还包含了编译器javac与自带的调试工具Jconsole、jstack等。 JRE(Java Runtime Environment):Java运行时环…

LeetCode 1775. 通过最少操作次数使数组的和相等 --双指针

通过最少操作次数使数组的和相等 中等 174 相关企业 给你两个长度可能不等的整数数组 nums1 和 nums2 。两个数组中的所有值都在 1 到 6 之间&#xff08;包含 1 和 6&#xff09;。 每次操作中&#xff0c;你可以选择 任意 数组中的任意一个整数&#xff0c;将它变成 1 到 6 …

Android9.0以上系统安装Edxposed

说明&#xff1a;仅供学习使用&#xff0c;请勿用于非法用途&#xff0c;若有侵权&#xff0c;请联系博主删除 作者&#xff1a;zhu6201976 一、背景说明 Android9.0以前&#xff0c;Xposed框架可通过apk进行快速安装&#xff0c;github地址&#xff1a; GitHub - rovo89/Xpos…

CRACK:CAD Exchanger SDK 3.15.0/MAC/WIN/LINUX/Android

CAD Exchanger SDK用于读取、写入和可视化 3D CAD 文件的软件库 通过访问 CAD 和 BIM 数据&#xff0c;快速轻松地丰富您的 Web、服务器或桌面应用程序。Ω578867473 使用 CATIA、SOLIDWORKS、Creo、STEP、JT、IFC 以及来自 C、Python、C#、Java 和 JavaScript 的更多格式。 适…

人民日报强烈推荐的13本证书,含金量都很高!

人民日报每年都会推荐一些当代最具含金量的证书&#xff0c;并建议大学生在大学期间的时候着手准备&#xff0c;为毕业后的简历添加色彩。 本次&#xff0c;人民日报推荐的证书主要有下列13种&#xff1a; 01 CPA&#xff08;注册会计师&#xff09; 含金量&#xff1a;★★…

博客管理系统

大致思路 1. 引入的依赖 数据库 Maven Repository: mysql mysql-connector-java 5.1.47 (mvnrepository.com) <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mys…

centos7用容器搭建svn仓库和管理页面

文章目录安装docker拉取 svn 仓库管理镜像创建仓库使用仓库安装docker 安装 docker 服务 yum install -y docker 修改 docker 服务配置&#xff0c;添加镜像拉取加速路径 vim /etc/docker/daemon.json javascript {"registry-mirrors": ["http://f1361db2.m.da…

【网页期末作业】基于HTML学校教育网页项目的设计与实现

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

更自动化的开发----给开发插上恣意飞翔的翅膀!

总结&#xff1a; 设置启动项&#xff1a;开机时候快速启动自己所需要的程序&#xff0c;根据程序数量不一样&#xff0c;每天节省可重复的劳动时间约为5s-1min(此处数据为自己瞎说 O(∩_∩)O哈哈~)&#xff1b; 使用生成代码的插件&#xff1a;开发使用easycode的插件&#…

m基于多D2D通信对和多蜂窝用户的LTE预编码技术matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 LTE网络中采用MIMO技术增加系统容量&#xff0c;提升吞吐率&#xff0c;从理论上来看,多天线的空分复用能成倍增加系统容量。但实际上并非如此,如&#xff0c;22MIMO的容量C&#xff08;容量&…

深度学习实验3 - 卷积神经网络

文章目录实验要求数据集定义1 手写二维卷积1.1 自定义卷积通道1.2 自定义卷积层1.3 添加卷积层导模块中1.4 定义超参数1.5 初始化模型、损失函数、优化器1.6 定义模型训练和测试函数&#xff0c;输出训练集和测试集的损失和精确度1.7 训练1.8 loss及acc可视化2 torch.nn 实现二…

一文打通ER图(手把手教你画)

期末了&#xff0c;E-R图也是大学课程设计中经常用到的&#xff0c;也是期末考的重点&#xff0c;毕竟大学生也没什么好考的&#xff0c;最近也有不少同学问&#xff0c;不少单子也扯到E-R图&#xff0c;但是我看了看网上的玩意好像没到手把手的地步&#xff0c;那么我就写一个…

Java面试题总结-ArrayList和LinkedList的区别

ArrayList和LinkedList都实现了List接口&#xff0c;并且两者都不是线程安全的。他们有以下的区别&#xff1a; &#xff08;1&#xff09;首先&#xff0c;最最本质的区别是&#xff1a;ArrayList内部使用的是动态数组来存储元素&#xff0c;而LinkedList内部使用的是双向链表…

PS图层+移动工具(3)对齐方式 对齐参照调整

此文为续文 请先查看 PS图层移动工具(2)复制删除快捷键 图层分组 前景色填充 后再查看本文 我们先来多选几个图层 然后上方属性栏 就激活了对应的操作 我们先来一波 左对齐 然后 就左对齐了 值得一提的是 这个左对齐 不是在屏幕的最左侧对齐 而是针对 所有你当前选择的图片…

华为机试 - 查找二叉树节点

题目描述 已知树形结构的所有节点信息,现要求根据输入坐标(x,y)找到该节点保存的内容值,其中x表示节点所在的层数,根节点位于第0层,根节点的子节点位于第1层,依次类推;y表示节点在该层内的相对偏移,从左至右,第一个节点偏移0,第二个节点偏移1,依次类推; 举例:上…

mysql索引失效

一、索引失效 1.当or左右查询字段只有一个是索引&#xff0c;该索引失效&#xff0c;只有当or左右查询字段均为索引时&#xff0c;才会生效 2.使用order by对数据库进行查询时&#xff0c;导致索引失效 &#xff0c;order by走全表扫描比回表的时间更少 3.主键和唯一索引在同…

算法刷题入门数据结构|二分查找

一.二分查找基础 1、二分查找介绍 二分查找(Binary search)也称折半查找&#xff0c;是一种效率较高的查找方法&#xff0c;时间复杂度。当对查数题目有时间复杂度要求是&#xff0c;首先就要考虑到二分查找。二分查找的思想很简单&#xff0c;属于分治策略的变种情况。但是&am…

贷后催收评分模型中的数据清洗与数据治理细节介绍

数据清洗是一个非常修炼身心的过程&#xff0c;途中你除了需要把所有的数据整业务合到一张宽表里。而这种宽表中所有的字段&#xff0c;是你理解完业务后&#xff0c;细心整理出来的所有适合建模的数据。 今天我们给大家介绍一下&#xff0c;在风控贷后评分模型中&#xff0c;…

C规范编辑笔记(七)

往期文章&#xff1a; C规范编辑笔记(一) C规范编辑笔记(二) C规范编辑笔记(三) C规范编辑笔记(四) C规范编辑笔记(五) C规范编辑笔记(六) 正文&#xff1a; 大家好&#xff0c;今天来分享一下C语言规范编辑笔记的第七篇&#xff0c;分享这个是希望自己后面忘记了可以去复习…