双周赛118(模拟、分组循环、记忆化搜索==>动态规划、单调队列优化DP)

news2024/11/30 7:50:16

文章目录

  • 双周赛118
    • [100121. 查找包含给定字符的单词](https://leetcode.cn/problems/find-words-containing-character/)
      • 模拟
    • [100138. 最大化网格图中正方形空洞的面积](https://leetcode.cn/problems/maximize-area-of-square-hole-in-grid/)
      • 题意转换 + 分组循环
    • [100133. 购买水果需要的最少金币数](https://leetcode.cn/problems/minimum-number-of-coins-for-fruits/)
      • 记忆化搜索(枚举买还是不买)
      • 记忆化搜索(枚举买哪个)==>动态规划(更优雅的解法)
      • 单调队列优化DP
    • [100135. 找到最大非递减数组的长度](https://leetcode.cn/problems/find-maximum-non-decreasing-array-length/)
      • 单调队列优化DP

双周赛118

100121. 查找包含给定字符的单词

简单

给你一个下标从 0 开始的字符串数组 words 和一个字符 x

请你返回一个 下标数组 ,表示下标在数组中对应的单词包含字符 x

注意 ,返回的数组可以是 任意 顺序。

示例 1:

输入:words = ["leet","code"], x = "e"
输出:[0,1]
解释:"e" 在两个单词中都出现了:"leet" 和 "code" 。所以我们返回下标 0 和 1 。

示例 2:

输入:words = ["abc","bcd","aaaa","cbc"], x = "a"
输出:[0,2]
解释:"a" 在 "abc" 和 "aaaa" 中出现了,所以我们返回下标 0 和 2 。

示例 3:

输入:words = ["abc","bcd","aaaa","cbc"], x = "z"
输出:[]
解释:"z" 没有在任何单词中出现。所以我们返回空数组。

提示:

  • 1 <= words.length <= 50
  • 1 <= words[i].length <= 50
  • x 是一个小写英文字母。
  • words[i] 只包含小写英文字母。

模拟

class Solution {
    public List<Integer> findWordsContaining(String[] words, char x) {
        List<Integer> res = new ArrayList<>();
        for(int i = 0; i < words.length; i++){
            if(words[i].indexOf(x) != -1)
                res.add(i);
        }
        return res;
    }
}

100138. 最大化网格图中正方形空洞的面积

中等

给你一个网格图,由 n + 2横线段m + 2竖线段 组成,一开始所有区域均为 1 x 1 的单元格。

所有线段的编号从 1 开始。

给你两个整数 nm

同时给你两个整数数组 hBarsvBars

  • hBars 包含区间 [2, n + 1]互不相同 的横线段编号。
  • vBars 包含 [2, m + 1]互不相同的 竖线段编号。

如果满足以下条件之一,你可以 移除 两个数组中的部分线段:

  • 如果移除的是横线段,它必须是 hBars 中的值。
  • 如果移除的是竖线段,它必须是 vBars 中的值。

请你返回移除一些线段后(可能不移除任何线段),剩余网格图中 最大正方形 空洞的面积,正方形空洞的意思是正方形 内部 不含有任何线段。

示例 1:

img

输入:n = 2, m = 1, hBars = [2,3], vBars = [2]
输出:4
解释:左边的图是一开始的网格图。
横线编号的范围是区间 [1,4] ,竖线编号的范围是区间 [1,3] 。
可以移除的横线段为 [2,3] ,竖线段为 [2] 。
一种得到最大正方形面积的方法是移除横线段 2 和竖线段 2 。
操作后得到的网格图如右图所示。
正方形空洞面积为 4。
无法得到面积大于 4 的正方形空洞。
所以答案为 4 。

示例 2:

img

输入:n = 1, m = 1, hBars = [2], vBars = [2]
输出:4
解释:左边的图是一开始的网格图。
横线编号的范围是区间 [1,3] ,竖线编号的范围是区间 [1,3] 。
可以移除的横线段为 [2] ,竖线段为 [2] 。
一种得到最大正方形面积的方法是移除横线段 2 和竖线段 2 。
操作后得到的网格图如右图所示。
正方形空洞面积为 4。
无法得到面积大于 4 的正方形空洞。
所以答案为 4 。

示例 3:

img

输入:n = 2, m = 3, hBars = [2,3], vBars = [2,3,4]
输出:9
解释:左边的图是一开始的网格图。
横线编号的范围是区间 [1,4] ,竖线编号的范围是区间 [1,5] 。
可以移除的横线段为 [2,3] ,竖线段为 [2,3,4] 。
一种得到最大正方形面积的方法是移除横线段 2、3 和竖线段 3、4 。
操作后得到的网格图如右图所示。
正方形空洞面积为 9。
无法得到面积大于 9 的正方形空洞。
所以答案为 9 。

提示:

  • 1 <= n <= 109
  • 1 <= m <= 109
  • 1 <= hBars.length <= 100
  • 2 <= hBars[i] <= n + 1
  • 1 <= vBars.length <= 100
  • 2 <= vBars[i] <= m + 1
  • hBars 中的值互不相同。
  • vBars 中的值互不相同。

题意转换 + 分组循环

https://leetcode.cn/problems/maximize-area-of-square-hole-in-grid/solutions/2542812/heng-shu-fen-bie-tong-ji-fen-zu-xun-huan-nboj/

class Solution {
    /**
    考虑最大矩形面积,再考虑正方形的面积
        矩形面积是长和宽的乘积
    横线竖线相互独立,以hBars为例
        如果不做任何移除,那么最长长度为 1。
        如果移除一条线,那么最长长度为 2。
        如果移除两条编号相邻的线,那么最长长度为 3。
        如果移除三条编号连续的线,那么最长长度为 4。
        依此类推。
    把hBars排序,找连续递增最长字段,子段+1就是这条边的最长长度
    求出后,正方形的边长是长宽的最小值
     */
    public int maximizeSquareHoleArea(int n, int m, int[] hBars, int[] vBars) {
        int size = Math.min(f(hBars), f(vBars));
        return size * size;
    }

    // 找连续递增最长字段
    public int f(int[] nums){
        Arrays.sort(nums);
        int ans = 0, i = 0;
        int n = nums.length;
        while(i < n){
            int start = i;
            i += 1;
            while(i < n && nums[i] - nums[i-1] == 1)
                i++;
            ans = Math.max(ans, i - start);
        }
        return ans + 1;
    }
}

100133. 购买水果需要的最少金币数

中等

你在一个水果超市里,货架上摆满了玲琅满目的奇珍异果。

给你一个下标从 1 开始的数组 prices ,其中 prices[i] 表示你购买第 i 个水果需要花费的金币数目。

水果超市有如下促销活动:

  • 如果你花费 price[i] 购买了水果 i ,那么接下来的 i 个水果你都可以免费获得。

注意 ,即使你 可以 免费获得水果 j ,你仍然可以花费 prices[j] 个金币去购买它以便能免费获得接下来的 j 个水果。

请你返回获得所有水果所需要的 最少 金币数。

示例 1:

输入:prices = [3,1,2]
输出:4
解释:你可以按如下方法获得所有水果:
- 花 3 个金币购买水果 1 ,然后免费获得水果 2 。
- 花 1 个金币购买水果 2 ,然后免费获得水果 3 。
- 免费获得水果 3 。
注意,虽然你可以免费获得水果 2 ,但你还是花 1 个金币去购买它,因为这样的总花费最少。
购买所有水果需要最少花费 4 个金币。

示例 2:

输入:prices = [1,10,1,1]
输出:2
解释:你可以按如下方法获得所有水果:
- 花 1 个金币购买水果 1 ,然后免费获得水果 2 。
- 免费获得水果 2 。
- 花 1 个金币购买水果 3 ,然后免费获得水果 4 。
- 免费获得水果 4 。
购买所有水果需要最少花费 2 个金币。

提示:

  • 1 <= prices.length <= 1000
  • 1 <= prices[i] <= 105

记忆化搜索(枚举买还是不买)

class Solution {
    int[] prices;
    int[][] cache;
    public int minimumCoins(int[] prices) {
        this.prices = prices;
        int n = prices.length;
        cache = new int[n][2100];
        for(int i = 0; i < n; i++)
            Arrays.fill(cache[i], -1);
        return dfs(0, 0);
    }

    /**
    定义 dfs(i, free) 表示当前购买到i,能免费购买的水果编号<free,所需要的最少金币数
     */
    public int dfs(int i, int free){
        if(i == prices.length)
            return 0;
        if(cache[i][free] >= 0) return cache[i][free];
        int res = Integer.MAX_VALUE / 2;
        // 买
        res = Math.min(res, dfs(i+1, i + i + 1 + 1) + prices[i]);
        // 不买
        if(free > i)
            res = Math.min(res, dfs(i+1, free));
        return cache[i][free] = res;
    }
}

记忆化搜索(枚举买哪个)==>动态规划(更优雅的解法)

https://leetcode.cn/problems/minimum-number-of-coins-for-fruits/solutions/2542044/dpcong-on2-dao-onpythonjavacgo-by-endles-nux5/

class Solution {
    int[] prices;
    int[] cache;
    public int minimumCoins(int[] prices) {
        int n = prices.length;
        this.prices = prices;
        cache = new int[n];
        Arrays.fill(cache, -1);
        return dfs(1);
    }

    /**
    定义 dfs(i) 表示获得第i个以及后面的水果所需要的最少金币数,i从1开始
    转移 
        枚举下一个需要购买的水果j,范围 [i+1, 2i+1]
    所有情况取最小值 即 dfs(i) = prices[i] + min(dfs(j))  j [i+1, 2i+1]
    递归边界: dfs(i) = prices[i], 2i>=n 「2i>n时,后面的水果都可以免费获得」
    递归入口:dfs(1)
     */
    public int dfs(int i){
        if(i * 2 >= prices.length)
            return prices[i-1];
        if(cache[i] >= 0)
            return cache[i];
        int res = Integer.MAX_VALUE;
        for(int j = i + 1; j <= i * 2 + 1; j++)
            res = Math.min(res, dfs(j));
        return cache[i] = res + prices[i-1];
    }
}

转递推

class Solution {
    public int minimumCoins(int[] prices) {
        int n = prices.length;
        for(int i = (n+1)/2-1; i > 0; i--){
            int mn = Integer.MAX_VALUE;
            for(int j = i; j <= i*2; j++)
                mn = Math.min(mn, prices[j]);
            prices[i-1] += mn;
        }
        return prices[0];
    }
}

单调队列优化DP

class Solution {
    /**
    j [i+1, 2i+1]
    注意到随着i变小,j的范围也在变小,计算min(dfs(j))的过程类似求滑动窗口最小值
    
    单调队列(单增)的原则 左边的小淘汰掉右边的大
     */
    public int minimumCoins(int[] prices) {
        int n = prices.length;
        Deque<int[]> dq = new ArrayDeque<>();
        dq.addLast(new int[]{n+1, 0}); // 哨兵 [下标,f[i]]
        // 队首在左边,队尾在右边
        for(int i = n; i > 0; i--){
            // 弹出离开窗口的元素
            while(dq.peekLast()[0] > i * 2 + 1){ // 右边离开窗口
                dq.pollLast();
            }
            // 每次转移只需要取队尾的数,它一定是最小的数
            int f = prices[i-1] + dq.peekLast()[1];
			
            while(f <= dq.peekFirst()[1]){
                dq.pollFirst();
            }
            dq.addFirst(new int[]{i, f}); // 左边进入窗口
        }
        return dq.peekFirst()[1];
    }
}

100135. 找到最大非递减数组的长度

困难

给你一个下标从 0 开始的整数数组 nums

你可以执行任意次操作。每次操作中,你需要选择一个 子数组 ,并将这个子数组用它所包含元素的 替换。比方说,给定数组是 [1,3,5,6] ,你可以选择子数组 [3,5] ,用子数组的和 8 替换掉子数组,然后数组会变为 [1,8,6]

请你返回执行任意次操作以后,可以得到的 最长非递减 数组的长度。

子数组 指的是一个数组中一段连续 非空 的元素序列。

示例 1:

输入:nums = [5,2,2]
输出:1
解释:这个长度为 3 的数组不是非递减的。
我们有 2 种方案使数组长度为 2 。
第一种,选择子数组 [2,2] ,对数组执行操作后得到 [5,4] 。
第二种,选择子数组 [5,2] ,对数组执行操作后得到 [7,2] 。
这两种方案中,数组最后都不是 非递减 的,所以不是可行的答案。
如果我们选择子数组 [5,2,2] ,并将它替换为 [9] ,数组变成非递减的。
所以答案为 1 。

示例 2:

输入:nums = [1,2,3,4]
输出:4
解释:数组已经是非递减的。所以答案为 4 。

示例 3:

输入:nums = [4,3,2,6]
输出:3
解释:将 [3,2] 替换为 [5] ,得到数组 [4,5,6] ,它是非递减的。
最大可能的答案为 3 。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

单调队列优化DP

单调队列需要思考清楚三步

  1. 转移之前,去掉队首无用数据

  2. 计算转移

  3. 去掉队尾无用数据

class Solution {
    /**
    划分型DP
    DFS最后一段从 i 到 n-1
    定义 f(i) 表示操作下标 0~i 的最长长度
        last[i] 表示这个操作后,最后一个数字的大小
        在f[i]尽量大的前提下,last[i]越小越好
        s[]前缀和 s[i]-s[j]表示从 j+1 到 i 的元素和
         6 5  1 9
    f    1 1  2 3
    last 6 11 6 9

    f[i] = (f[j]+1 , 把j+1到i的这一段合并成一个数
    s[i]-s[j] >= last[j] =>  s[i] >= last[j] + s[j]
    如何找到关系?

    考虑两个转移来源j和k,如果j < k且s[j]+last[j] >= s[k]+last[k]  
        这意味着如果能从f[j]转移到f[i],那么也一定能从f[k]转移到f[i]
        又由于f[j]<=f[k],所以永远不需要从f[j]转移到f[i]
    所以可以用单调队列来维护j,满足从队首到队尾的j和s[j]+last[j]都是严格递增的

    单调队列需要思考清楚三步
    1. 转移之前,去掉队首无用数据
        由于i越大s[i]越大,满足s[j]+last[j]<=s[i]的j也越大,转移来源f[j]也越大
    2. 计算转移
        从单调队列中找到最大的j,满足s[j]+last[j]<=s[i]
            ==>f[i] = f[j]+1和last[i] = s[i]-s[j]
    3. 去掉队尾无用数据
        把i加入队尾,在此之前弹出s[j]+last[j] >= s[i]+last[i] 的j
     */
    public int findMaximumLength(int[] nums) {
        int n = nums.length;
        long[] s = new long[n + 1];
        int[] f = new int[n + 1];
        long[] last = new long[n + 1];
        int[] q = new int[n + 1]; // 数组模拟队列
        int front = 0, rear = 0;
        for (int i = 1; i <= n; i++) {
            s[i] = s[i - 1] + nums[i - 1];
            
            // 1. 去掉队首无用数据(计算转移时,直接取队首)
            while (front < rear && s[q[front + 1]] + last[q[front + 1]] <= s[i]) {
                front++;
            }
            
            // 2. 计算转移
            f[i] = f[q[front]] + 1; 
            last[i] = s[i] - s[q[front]];
            
            // 3. 去掉队尾无用数据
            while (rear >= front && s[q[rear]] + last[q[rear]] >= s[i] + last[i]) {
                rear--;
            }
            q[++rear] = i;
        }
        return f[n];
    }
}

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

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

相关文章

鸿蒙(HarmonyOS)应用开发——基础语法例子

前言 在前面几篇文章中&#xff0c;已经介绍了ArkTs中装饰器、声明式UI、自定义组件。知识都是很零散的&#xff0c;我们可以做一个Demo。现在我们一步一步完成下面这样的页面 创建ToDo项目 输入项目的名称&#xff0c;存放的位置&#xff0c;点击完成。IDE创建项目&#xf…

MySQL基本SQL语句(下)

MySQL基本SQL语句&#xff08;下&#xff09; 一、扩展常见的数据类型 1、回顾数据表的创建语法 基本语法&#xff1a; mysql> create table 数据表名称(字段名称1 字段类型 字段约束,字段名称2 字段类型 字段约束,...primary key(主键字段 > 不能为空、必须唯一) ) …

2018年11月8日 Go生态洞察:参与2018年Go用户调查

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

共享模型之无锁

目录 无锁实现线程安全 无锁与synchronized效率对比 原子整数 原子引用类型 ABA问题 原子数组 字段更新器 原子累加器LongAdder LongAdder源码分析 Unsafe cas修改对象属性值 案例 对于银行取钱来说&#xff0c;需要保证线程安全&#xff0c;一个1w的账户由1k个线程…

GEE 22:基于GEE实现物种分布模型(更新中。。。。。。)

物种分布模型 1. 数据点准备1.1 数据加载1.2 去除指定距离内的重复点1.3 定义研究区范围1.4 选择预测因子 1. 数据点准备 1.1 数据加载 首先需要将CSV文件导入到GEE平台中&#xff0c;同样也可以导入shp格式文件。 // 1.Loading and cleaning your species data *************…

【KubeSphere】基于AWS在 Linux 上以 All-in-One 模式安装 KubeSphere

文章目录 一、实验配置说明二、实验准备工作1.确认系统版本2. 修改网络DNS3. 关闭SELINUX4. 关闭防火墙 三、实验依赖项安装四、下载 KubeKey五、一键化安装部署六、验证安装结果七、登录KubeSphere管理控制台八、参考链接 一、实验配置说明 本实验基于AWS启动一台新实例&…

@Async注解的坑,小心

背景 前段时间&#xff0c;一个同事小姐姐跟我说她的项目起不来了&#xff0c;让我帮忙看一下&#xff0c;本着助人为乐的精神&#xff0c;这个忙肯定要去帮。 于是&#xff0c;我在她的控制台发现了如下的异常信息&#xff1a; Exception in thread "main" org.s…

信创之国产浪潮电脑+统信UOS操作系统体验7:VSCode任务tasks.json的问题匹配器problemMatcher详解

☞ ░ 前往老猿Python博客 ░ https://blog.csdn.net/LaoYuanPython 一、引言 最近在国产操作系统上使用Visual Studio Code的任务配置&#xff0c;发现tasks下的问题匹配器problemMatcher公开资料很少或很简单&#xff0c;直接在某度上通过problemMatcher搜索基本上没有精确…

从代码执行,看单片机内存的分配

1、单片机执行指令过程详解 单片机执行程序的过程&#xff0c;实际上就是执行我们所编制程序的过程。即逐条指令的过程。计算机每执行一条指令都可分为三个阶段进行&#xff0c;即取指令--分析指令--执行指令。 取指令的任务是&#xff1a;根据程序计数器PC中的值从程序存储器读…

粒子群算法Particle Swarm Optimization (PSO)的定义,应用优点和缺点的总结!!

文章目录 前言一、粒子群算法的定义二、粒子群算法的应用三、粒子群算法的优点四、粒子群算法的缺点&#xff1a;粒子群算法的总结 前言 粒子群算法是一种基于群体协作的随机搜索算法&#xff0c;通过模拟鸟群觅食行为而发展起来。该算法最初是由Eberhart博士和Kennedy博士于1…

由于找不到msvcp120.dll无法继续执行代码是什么原因怎么修复

今天我想和大家分享的是关于“msvcp120.dll丢失的解决方法”。或许有些同学在平时使用电脑的过程中会遇到这个问题&#xff0c;但是并不知道该如何解决。那么&#xff0c;接下来我将从三个方面为大家介绍&#xff1a;msvcp120.dll丢失的原因、msvcp120.dll是什么以及msvcp120.d…

小程序如何进行版本升级

小程序版本升级是非常重要的&#xff0c;它可以帮助商家及时更新功能、修复bug&#xff0c;提升用户体验&#xff0c;增加小程序的竞争力。那么&#xff0c;商家怎么进行小程序版本升级呢&#xff1f;下面具体介绍。 在小程序管理员后台->版本设置处&#xff0c;会显示是否…

基于springboot+vue的学生宿舍管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

NEFU离散数学实验PBL

1.青蛙的约会 Description 两只青蛙在网上相识了&#xff0c;它们聊得很开心&#xff0c;于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上&#xff0c;于是它们约定各自朝西跳&#xff0c;直到碰面为止。可是它们出发之前忘记了一件很重要的事情&#xff0c;既…

案例029:基于微信小程序的阅读网站设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

[leetCode]257. 二叉树的所有路径(两种方法)

257. 二叉树的所有路径 题目描述&#xff1a; 给你一个二叉树的根节点 root &#xff0c;按 任意顺序 &#xff0c;返回所有从根节点到叶子节点的路径。 叶子节点 是指没有子节点的节点。 示例&#xff1a; 输入&#xff1a;root [1,2,3,null,5]输出&#xff1a;["1-&g…

超实用!Spring Boot 常用注解详解与应用场景

目录 一、Web MVC 开发时&#xff0c;对于三层的类注解 1.1 Controller 1.2 Service 1.3 Repository 1.4 Component 二、依赖注入的注解 2.1 Autowired 2.2 Resource 2.3 Resource 与 Autowired 的区别 2.3.1 实例讲解 2.4 Value 2.5 Data 三、Web 常用的注解 3.1…

Echarts 设备状态 甘特图

在做工厂智能化生产看板时&#xff0c;绝对会有设备状态看板&#xff0c;展示设备当天或者当前状态&#xff0c;设备状态数据一般是有mes 系统设备管理模块对设备信息进行采集&#xff0c;一般包括过站数据&#xff0c;设备当前状态&#xff0c;是否在线是否故障、检修、待生产…

解决Vue编程式导航路由跳转不显示目标路径问题

我们配置一个编程式导航的路由跳转&#xff0c;跳转到 /search 页面&#xff0c;并且携带categoryName和categoryId两个query参数。 this.$router.push({path: "/search",query: {categoryName: dataset.categoryname,categoryId: dataset.categoryid} }) 如果我们…

清华提出 SoRA,参数量只有 LoRA 的 70%,表现更好!

现在有很多关于大型语言模型&#xff08;LLM&#xff09;的研究&#xff0c;都围绕着如何高效微调展开。微调是利用模型在大规模通用数据上学到的知识&#xff0c;通过有针对性的小规模下游任务数据&#xff0c;使模型更好地适应具体任务的训练方法。 在先前的工作中&#xff…