【每日一题】23年4月

news2025/1/15 23:27:07

文章目录

  • C++ 技术点
  • 多边三角形剖分的最低得分(dp思路,选不选问题)
  • 移动石子到连续(思路)
  • 1027. 最长等差数列(动态规划)
  • 1105. 填充书架(动态规划)
  • 1031 两个非重叠子数组的最大和
  • 1163.按字典序排在最后的子串(双指针,反证法)
  • 1187 使数组严格递增(dp)

C++ 技术点

1. string类型使用find函数。
int index = s.find("@");
if (inde != string:npos){
xx
}

2. transform函数,将整个字符串做整体改变。
transform(s.begin(), s.end(), s.begin(), ::tolower);

多边三角形剖分的最低得分(dp思路,选不选问题)

在这里插入图片描述
在这里插入图片描述

  • dp定义:dp[i][j]表示点 i 到点 j 的多边形的最小值。
  • dp转移:转移方程的思路两种。选不选、选哪个。现在本题只有选哪个这个思路。因此dp[i][j] = min(dp[i][j], dp[i,k]+dp[k,j]+cal(i,j,k))
  • 枚举次序:求解dp[i][j]的时候是需要提前知道dp[i][k](k<j)这个更小的问题的,因此此时 j 是正序枚举;同样的对于dp[k][j]则是需要知道 k 才能知道 i 的,因此 i 是倒序枚举
  • dp初始值:在枚举次序分析以后,我们知道dp[i][i],dp[i][i+1] = 0, 其他的初始化为INT_MAX;
class Solution {
public:
    int minScoreTriangulation(vector<int> &v) {
        int n = v.size(), f[n][n];
        memset(f, 0, sizeof(f));
        for (int i = n - 3; i >= 0; --i)
            for (int j = i + 2; j < n; ++j) {
                f[i][j] = INT_MAX;
                for (int k = i + 1; k < j; ++k)
                    f[i][j] = min(f[i][j], f[i][k] + f[k][j] + v[i] * v[j] * v[k]);
            }
        return f[0][n - 1];
    }
};


移动石子到连续(思路)

在这里插入图片描述

比较有思维量的题目。首先我们需要思考下最终的结果是处于什么区间上的,也就是我们需要选在一个长度为 k 的区间最后将所有的石子搬运到这个区间上。

另外我们还需要思考,如何实现最大和最小的移动。

  • 最大:以左右端点作为其中一个端点进行移动。这两个点之间的所有端点都是需要一次操作的。
  • 最小:首先设计一个滑动窗,窗的两侧为实际存在的石子,并且左右两侧的石子的距离是小于 n 的最大距离。此时我们可以分情况讨论:
    • 如果左右端点差为n,完全不需要动,返回0;
    • 两个端点的距离差[stone[l], stone[r] ]恰好为n-1,石子数量也为n-1,也就是n-1个全部都是连续的。此时外面还剩一个,无论在什么位置,最多两次就可以连续。(认为右侧不动)
    • 否则,填上中间的空挡即可。
class Solution {
public:
    vector<int> numMovesStonesII(vector<int>& stones) {
        int n = stones.size();
        sort(stones.begin(), stones.end());
        // max = max(stones[n-2] - stones[0] -1, stones[n-1]-stones[1]-1)
        // min = 在一个滑动窗内 k个,因此需要n-k次 
        // 如果有n-1个是紧密相连的。那么 需要两次

        if(stones[n-1]- stones[0]+1 == n){
            return {0, 0};
        }

        int max_v = max(stones[n-2] - stones[0] +1, stones[n-1]-stones[1]+1)-(n-1);
        int min_v = max_v;
        int r = 1;
        for (int l = 0;l<n;l++){
            while(r < n && stones[r]-stones[l]+1 <= n){
                r++;
            }
            r--;
            // 左端点l到右端点r是连续的
            if (r-l+1 == n-1 && stones[r]-stones[l]+1 == n-1){
                min_v = min(min_v, 2);
            }else{
                min_v = min(min_v, n-(r-l+1));
            }
        }
        return {min_v, max_v};
    }
};

1027. 最长等差数列(动态规划)

在这里插入图片描述

最长子序列的问题很容易想到dp问题。选哪个问题

  • dp定义:dp[i][j] 长度为 i 时,差值为 j 时的最大长度
  • dp转移:dp[i][j] = max(dp[k][j]+(k,i)*maxValue, dp[i][j])
  • dp初始值:0
  • dp转移顺序:正序遍历 i ,反向遍历 k
class Solution {
public:
    int longestArithSeqLength(vector<int> &a) {
        int ans = 0, n = a.size(), f[n][1001];
        memset(f, 0, sizeof(f));
        for (int i = 1; i < n; ++i)
            for (int j = i - 1; j >= 0; --j) {
                int d = a[i] - a[j] + 500; // +500 防止出现负数
                if (f[i][d] == 0) {
                    f[i][d] = f[j][d] + 1; // 默认的 1 在下面返回时加上
                    ans = max(ans, f[i][d]);
                }
            }
        return ans + 1;
    }
};

1105. 填充书架(动态规划)

在这里插入图片描述
选不选?选哪个? 选哪个问题

  • dp定义:dp[i]表示前 i 本 书的最小高度。
  • dp转移:dp[i] = min(dp[i], dp[j]+cal(j,i))。对于后 j 到 i 的书我们放在一个全新的书架上。
  • dp状态:初始化为INT_MAX,但是dp[0] = 0;
class Solution {
public:
    int minHeightShelves(vector<vector<int>>& books, int shelfWidth) {
        // dp[i] 表示前i本书,总的高度
        // dp[i] 选什么?当前层还是下一层? X 
        // 选哪个?从哪个开始放置在新的一层? ✅
        int n = books.size();
        vector<int> dp(n+1, INT_MAX);
        dp[0] = 0; // 初始化为0
        for (int i = 1;i<=n;i++){
            int curmax = 0;
            int curlength = 0;
            for (int j = i;j>=1;j--){ // 选择某一个点,这个点之后都放在某一层
                curlength += books[j-1][0];
                // 超出了就停止,此时无法都放在一层
                if(curlength > shelfWidth) break;
                curmax = max(curmax, books[j-1][1]);
                dp[i] = min(dp[i], dp[j-1]+curmax);
            }
        }
        return dp[n];
    }
};

1031 两个非重叠子数组的最大和

在这里插入图片描述
依然会有一点类似滑动窗口的思路,我们枚举second的起始位置 i ,可以表示firstLen的选取在[0,i]之间实现,second就是[i+1, i+secondLen]。 对于firsr 和 second可以交换前后的情况,我们可以分两种情况讨论即可。

class Solution {
public:
    int help(vector<int>& nums, int firstLen, int secondLen) {

        // dp[i] 表示前i个元素时,first数组的最大值,
        // ans = dp[i]+sum(nums(i+1, i+secondLen))
        int suml = accumulate(nums.begin(), nums.begin() + firstLen, 0);
        int maxSumL = suml;
        int sumr = accumulate(nums.begin() + firstLen, nums.begin() + firstLen + secondLen, 0);
        int res = maxSumL + sumr;
        // i表示second的结尾位置,j表示first的结尾位置
        for (int i = firstLen + secondLen, j = firstLen; i < nums.size(); ++i, ++j) {
            suml += nums[j] - nums[j - firstLen];
            maxSumL = max(maxSumL, suml);
            sumr += nums[i] - nums[i - secondLen];
            res = max(res, maxSumL + sumr);
        }
        return res;
    }

    int maxSumTwoNoOverlap(vector<int>& nums, int firstLen, int secondLen) {
    // 分两种情况分别讨论
        return max(help(nums, firstLen, secondLen), help(nums, secondLen, firstLen));
    }
};

1163.按字典序排在最后的子串(双指针,反证法)

在这里插入图片描述

这个题目的思路很独特,不知道更深层的思想是什么。首先有一个证明,答案一定是后缀字符串。只是我们要去选择这个开头的位置。因此我们可以比较不同的开头。

我们维护一个双指针,假设 i 是最佳的答案,我们尝试在 i 的后面寻找到一个 j 去证明 i 不是最好。


class Solution {
public:
    string lastSubstring(string s) {
        int n = s.size();
        int i = 0;
        int j = 1;
        int k = 0;
        // 假设 i 是最好的点,我们尝试找一个点j去推翻,如果找不到,就是最好的点。
        // i表示当前最大的字串开始的位置,j是当前考虑的字串的位置,k是当前比较的位置
        while( j + k < n) {
            if (s[i + k] == s[j + k]) {
                ++k;
            } else if (s[i + k] < s[j + k]) {
                //因为i+k这个点不如j+k这个点,因此此时s[i+1,..,i+k]开头的的字串都不如s[j+1,..,j+k]开头的字串更好。都不考虑了,直接跳到新的点去论证。
                i += k + 1;
                k = 0;
                // 如果i在j的位置以后了,我们更新j比较。前面的点都被推翻了。
                if (i >= j) {
                    j = i + 1;
                }
            } else {
                // s[i + k] > s[j + k] 我们需要找新的点去推翻,这个点就是j+k+1才可能
                j += k + 1;
                k = 0;
            }
        }
        return s.substr(i);
    }
};

1187 使数组严格递增(dp)

**在这里插入图片描述**

  • dp[i][j] 表示对于前 i 个数字时候,替换了 j 次时的最后一个数字。这个定义比较奇特,没有直接定义所求内容。
  • 转移方程:
    • 如果arr[i] > dp[i-1][j] 那可以直接,dp[i][[j] = arr[i];
    • 对于无法选择arr[i]或者不选择arr[i]的情况, 在arr2[j, end]里面寻找到第一个大于(upperbound)的 dp[i-1][j-1]的
  • 初始:对于i= 0这个位置,dp[0][0] = arr[0], dp[0][1] = arr2[0], 其余的都初始化为int_MAX;
class Solution {
public:
    int makeArrayIncreasing(vector<int>& arr1, vector<int>& arr2) {
        // dp[i][j] 表示对于对于前i个arr1,替换j次时候的最小后缀;
        // dp[i][j] = arr[i] 满足arr[i] > dp[i-1][j] 不进行替换
        // dp[i][j] = min(dp[i][j], cal) 其中 cal是arr2中 index大于等于j,val严格大于dp[i-1][j-1]的最小值 j>0
        // 初始化:dp[i][j] = INF dp[0][0] = arr[0] dp[0][1] =  arr2[0]
        sort(arr2.begin(), arr2.end());
        arr2.erase(unique(arr2.begin(), arr2.end()), arr2.end());// 去重
        int n = arr1.size();
        int m = min((int)arr2.size(), n);
        vector<vector<int>> dp(n, vector<int>(m+1, INT_MAX));

        dp[0][0] = arr1[0];
        dp[0][1] = arr2[0];
        for(int i = 1;i<n;i++){
            for(int j = 0;j<=min(i+1,m);j++){
                if(arr1[i] > dp[i-1][j]) {
                    dp[i][j] = arr1[i];
                }
                if(j>0){
                    auto it = upper_bound(arr2.begin()+j-1, arr2.end(), dp[i-1][j-1]);
                    if(it != arr2.end()){
                        dp[i][j] = min(dp[i][j], *it);
                    }
                    
                }
                if(dp[n-1][j] != INT_MAX) return j;
            }
        }
        return -1;
    }
};

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

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

相关文章

【Java 】从源码全面解析Java 线程池

文章目录 一、引言二、使用三、源码1、初始化1.1 拒绝策略1.1.1 AbortPolicy1.1.2 CallerRunsPolicy1.1.3 DiscardOldestPolicy1.1.4 DiscardPolicy1.1.5 自定义拒绝策略1.2 其余变量 2、线程池的execute方法3、线程池的addWorker方法3.1 校验3.2 添加线程 4、线程池的 worker …

PostgreSQL 基础知识:psql 提示和技巧

对于积极使用和连接到 PostgreSQL 数据库的任何开发人员或 DBA 来说&#xff0c;能够访问psql命令行工具是必不可少的。在我们的第一篇文章中&#xff0c;我们讨论了 psql的简要历史&#xff0c;并演示了如何在您选择的平台上安装它并连接到 PostgreSQL 数据库。 在本文中&…

使用腾讯云快速完成网站备案的详细过程

最近总是被备案弄得血压飙升&#xff0c;明明是一件很简单的事情&#xff0c;不知道大家为什么搞得那么复杂&#xff0c;首先了解下为什么要备案&#xff0c;根据国务院令第292号《互联网信息服务管理办法》和 《非经营性互联网信息服务备案管理办法》规定&#xff0c;国家对经…

【TCP四次挥手】

文章目录 TCP 四次挥手过程是怎样的&#xff1f;为什么挥手需要四次&#xff1f;第一次挥手丢失了&#xff0c;会发生什么&#xff1f;第二次挥手丢失了&#xff0c;会发生什么&#xff1f;第三次挥手丢失了&#xff0c;会发生什么&#xff1f;第四次挥手丢失了&#xff0c;会发…

Lecture 13(Extra Material):Q-Learning

目录 Introduction of Q-Learning Tips of Q-Learning Double DQN Dueling DQN Prioritized Reply Multi-step Noisy Net Distributional Q-function Rainbow Q-Learning for Continuous Actions Introduction of Q-Learning Critic: The output values of a critic…

为生信写的Python简明教程 | 视频3

开源生信 Python教程 生信专用简明 Python 文字和视频教程 源码在&#xff1a;https://github.com/Tong-Chen/Bioinfo_course_python 目录 背景介绍 编程开篇为什么学习Python如何安装Python如何运行Python命令和脚本使用什么编辑器写Python脚本Python程序事例Python基本语法 数…

PySpark基础入门(7):Spark SQL

概述 SparkSQL和Hive的异同 Hive和Spark 均是&#xff1a;“分布式SQL计算引擎”SparkSQL使用内存计算&#xff0c;而Hive使用磁盘迭代&#xff0c;所以SparkSQL性能较好二者都可以运行在YARN之上SparkSQL无元数据管理&#xff0c;但可以和hive集成&#xff0c;集成之后可以借…

极光笔记 | 极光推出“运营增长”解决方案,开启企业增长新引擎

摘要&#xff1a; 移动互联网流量红利见底&#xff0c;营销获客面临更多挑战 随着移动互联网流量红利见顶&#xff0c;越来越多的企业客户发现获取新客户的难度直线上升&#xff0c;获客成本持续攀高。 传统的移动互联网营销以PUSH为代表&#xff0c;采用简单粗暴的方式给用户…

PaddleVideo 简介以及文件目录详解

简介特性许可证书 PaddleVideo 文件目录总述applications 文件夹详述configs 文件夹详述docs 文件夹详述paddlevideo 文件夹详述utils 文件夹tasks 文件夹loader 文件夹modeling 文件夹solver 文件夹metrics 文件夹 简介 PaddleVideo 旨在打造一套丰富、领先且实用的 Video 工…

【阿里云】秒懂云通信

目录 一、秒懂云通信-第一回听什么? 二、短信的使用场景 1. 短信的三种类型&#xff1a;短信通知、验证、会员营销 三、短信平台的选择 1、看成功率 2、看价格 3、看体验 四、秒懂云通信 五、如何使用 Step 1&#xff1a;业务入口 Step 2&#xff1a;注册账号 Step…

云安全技术——Snort安装与配置

目录 一、Snort简介 二、安装Centos7 Minimal系统 三、基本环境配置 四、安装Snort 五、下载规则 六、配置Snort 七、测试Snort 一、Snort简介 Snort是一个开源的网络入侵检测系统&#xff0c;主要用于监控网络数据包并检测可能的攻击行为。它可以实时分析网络流量&…

HJ37 统计每个月兔子的总数

HJ37 统计每个月兔子的总数 描述示例解题思路以及代码分析解法1解法2 描述 描述 有一种兔子&#xff0c;从出生后第3个月起每个月都生一只兔子&#xff0c;小兔子长到第三个月后每个月又生一只兔子。 例子&#xff1a;假设一只兔子第3个月出生&#xff0c;那么它第5个月开始会…

ASEMI代理ADUM3211TRZ-RL7原装ADI车规级ADUM3211TRZ-RL7

编辑&#xff1a;ll ASEMI代理ADUM3211TRZ-RL7原装ADI车规级ADUM3211TRZ-RL7 型号&#xff1a;ADUM3211TRZ-RL7 品牌&#xff1a;ADI/亚德诺 封装&#xff1a;SOIC-8 批号&#xff1a;2023 引脚数量&#xff1a;8 工作温度&#xff1a;-40C~125C 安装类型&#xff1a;表…

操作系统原理 —— 操作系统什么时候会发生进程的调度(十二)

操作系统什么时候需要进程调度&#xff1f; 进程调度的层次中&#xff0c;有一个低级调度&#xff0c;就是按照某种算法从就绪队列中选择一个进程为其分配 CPU。 那操作系统会在什么时候触发进程调度呢&#xff1f; 在这里一共可以分为两大类&#xff1a; 当前运行的进程主动…

04-微服务部署2023系列-centos安装gitlab

目的:为了将来的devops快速部署搭建自己的代码库,保证速度和私密性 前面01-03小节: 完成基本的服务器环境 centos_nginx_java_docker; 这个基础环境是将来集群中每台服务器的基本, 可以先打一个镜像备份。 阿里云的镜像备份比较简单。以后搭建新服务器时,以这个为基础,安…

JUC并发包详解AQS同步队列

一、AQS介绍 在JUC并发包中&#xff0c;AQS为其最关键的作用&#xff0c;全称为abstractQueuedSynchroinzed同步器&#xff0c;为信号量semaphore、同步锁的基础抽象类。 其中内部主要有二大块 state 共享资源&#xff0c;通过并发操作state修改改值为1&#xff0c;如果修改成…

《Linux 内核设计与实现》09. 内核同步介绍

共享资源之所以要防止并发访问&#xff0c;是因为如果多个执行线程同时访问和操作数据&#xff0c;就有可能发生各线程之间相互覆盖共享数据的情况&#xff0c;从而造成被访问的数据不一致状态。 临界区和竞争条件 临界区&#xff1a;访问和操作共享数据的代码段。原子操作&a…

键控流水灯

项目文件 文件 关于项目的内容知识点可以见专栏单片机原理及应用 的第四章 IO口编写 在电路图的基础上&#xff0c;编写可键控的流水灯程序。要求实现的功能为&#xff0c;K1是总开关,当K1首次按下时&#xff0c;流水灯由下往上流动;当K2按下时停止流动&#xff0c;且全部灯灭…

ASK,FSK和PSK

一、ASK&#xff0c;FSK和PSK 数字信号只有有限个离散值&#xff0c;使用数字信号对载波进行调制的方式称为键控(Keying),分为幅度键控&#xff08;ASK)、频移键控&#xff08;FSK)和相移键控&#xff08;PSK)。 幅度键控可以通过乘法器和开关电路来实现&#xff0c;在数字信…

SpringBoot【开发实用篇】---- 配置高级

SpringBoot【开发实用篇】---- 配置高级 1. ConfigurationProperties2. 宽松绑定/松散绑定3. 常用计量单位绑定4. 校验5. 数据类型转换 进入开发实用篇第二章内容&#xff0c;配置高级&#xff0c;其实配置在基础篇讲了一部分&#xff0c;在运维实用篇讲了一部分&#xff0c;这…