最近做到一道好题,特来和大家分享一下。

news2024/12/23 10:40:54

题目:299. 裁剪序列 - AcWing题库
分析:
题目给我们的数据范围是105;也就是说我们的时间复杂度要控制到O(nlog2n)才可以。假设每一个元素都可以作为一个区间,那么可以有

Cn1+……+Cnn= 2n-1种划分方法,n达到了105,很显然就超时。所以暴力枚举绝对会超时。所以我们需要优化。

优化主要采用DP或者贪心。可以先考虑DP,如果答案都是f[i]的一个子集里,那么我们就可以使用贪心。

DP我们这里采用闫氏DP分析法:
在这里插入图片描述
那f[i]集合如何划分?

每一次加入第i位置的元素,会对整体造成什么影响呢?---->最后一段的长度发生变化,那么最后一段的最大值也可能发生变化。或者说,因为i的加入,可能导致原来最后一段加上当前i位置的值大于m。这样就需要重新划分。所以说最后一段长度我们可以设为k:

在这里插入图片描述
对于不同的k,都会产生不同的结果。因此我们对于f[i]的子集划分就有了依据:
在这里插入图片描述
状态方程如何计算?最后一段选了长度为k的,最大值为amax,那么前面就是f[i - k].所以说最后的状态方程就是:
f [ i ] = m i n ( f [ i ] , f [ i − k ] + a m a x ) f[i] = min(f[i], f[i - k] + a_{max}) f[i]=min(f[i],f[ik]+amax)
我们再仔细的分析,发现我们循环i的时候,每次都要循环k,并且我们还需要在这k里找出来每种方案的最小值还是一层遍历,因此时间复杂度会达到O(n3)级别,还是会超时。当然我们在求每一种方案的最小值的时候,我们在循环k的时候设一个变量来保存最后一段为k的最小值,这样我们的时间复杂度就降到了O(n2).

因此我们还需要进行优化:

最后一段的长度是有限制的,区间内元素的和不能大于m。假设最后一段的长度达到最大,我们使用j来表示最后一段区间最左边的点,(如果是j-1,那么我们最后一段的和就要大于m):
在这里插入图片描述
当最后一段的长度达到最大值,最后一段左边最远的位置就是j,那么前面j-1的最小值就是f[j - 1].

对于当前的[j , i],区间内有最大值amax1如图:
在这里插入图片描述
当前的区间最大值就是amax1,那么这种划分的方案的结果为f[j - 1] + amax1.我们再看j的位置,如果j取到(j, k1]这个范围内j,首先最后一段的最大值是不会发生改变还是amax1。变化的是f[j - 1].所以我们目前需要分析f[j - 1]的变化。
在这里插入图片描述
f[j] ≥ f[i].对于这两段,首先可以是可以找到位置k,这两段的f[k]都是相等的。剩下的不就是最后一段的比较.题目里说元素都是非负数,所以(i, j]里的数与原来i的最后一段合并,最大值还是i区间里的最后一段的最大值;也可能使得元素之后大于m,在(i, j]里有产生了新的段,这一段的最大值也得是一个非负数,所以我们可以得到f[j] ≥ f[i].

好,我们现在回到这里:
在这里插入图片描述
这里j是最左边的位置,如果j在[j, k1]的范围内,最大值都是amax, 而变化的就是f[j - 1].j是不断变大的,但是我们要记住我们是求最后一段所有方案最大值中的最小值.所以这里我们贪心一下,j∈[j, k1]的时候f[j - 1] min = f[ j - 1].所以我们不需要枚举这一段里的j只需要有j即可。

那如果说j > k1呢?
在这里插入图片描述
j∈(k1, k2]的时候,那么最后一段区间的最大值就是amax2。利用我们上面分析amax1的结论,我们可以很容易得到:如果j∈(k1, k2],那么当前方案的最小值是f[k1] + amax2 (j的位置在k1 + 1处。)

所以我们依次推导:
在这里插入图片描述
我们发现最后一段我们只需要维护一如上图所示的单调队列即可。在这个单调队列里面,如果加入一个i,就需要将i加入队列即可。如果加入的i的值≥amax4,那么就把amax4的值弹出。就这样依次进行下去直到当前i的值 < 当前队尾的元素,这样就把当前i的值加入到队尾。

对于队头,我们还需要检查当前每次加入一个i的时候,当前队列的元素是不是大于m,如果大于m,j就需要往右移(也就是抛去队头)

对于每一段我们只需要取队头元素即可,因为队头元素是最后一段里最大的值。但是我们不要忘记了,我们要取的是最后一段所有方案的最大值中的最小值。也就是说我们还需要循环这样一个队列找到其中的最小值(f[j -1] + amax)。因为我们的时间复杂度还可能是O(n2)的。

因此我们需要维护一个集合,这个集合可以动态地增加一个数(队尾插入),删除一个数(队头删除),并且求最小值(f[j -1] + amax

我们可以使用堆来维护这样的一个集合,堆可以动态的增加一个数,删除一个数,并且求最小值,而且时间复杂度是O(log2n)的。这里堆只能删除头结点,不能删除任意的节点。(这里为什么不能用目前还未知,这个问题就先留着)

所以这里可以使用平衡树,可以动态的增加一个数,删除一个数,并且求最小值,时间复杂度都是O(log2n).

C++里可以使用set。这一题里集合里可能会出现相同的值,因此我们这里使用

ok!完美!时间复杂度成功降到题目要求的范围!

#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>

using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL f[N], m;
int n;
int w[N], q[N];
multiset<LL> S;
void remove(LL x)
{
    /*
    * 因为不同的节点可能会存储相同的值,因此这里只需要删除一个值就行
    * 使用S.erase(x);会删除所有等于x的元素。
    */
    auto it = S.find(x);
    S.erase(it);
}
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++) 
    {
        cin >> w[i];
        // 如果当前的元素>m那么这个元素可能不能属于任何一个组,那么就报错,就是-1
        if(w[i] > m)
        {
            cout << "-1" << endl;
            return 0;
        }
    }
    int hh = 0, tt = -1;
    LL sum = 0;
    for(int i = 1, j = 1; i <= n; i++)
    {
        sum += w[i];
        while(sum > m)
        {
            // 找当前最后一个区间的j的最小值。
            sum -= w[j++];
            if(hh <= tt && q[hh] < j) // 删除队头
            {
                // 只在队列中的相邻两个位置之间选择,如果只有一个元素,那么就直接加入multiset集合里就行
                // 这里的if做的就是删除队头的任务
                if(hh < tt) remove(f[q[hh]] + w[q[hh + 1]]);
                hh++;
            }
        }
        
        // 往队尾插入元素
        while (hh <= tt && w[q[tt]] <= w[i] )
        {
            // 删除一个队尾的元素,其实也得删除它在集合multiset中对应的元素。
            if(hh < tt) remove(f[q[tt - 1]] + w[q[tt]]);
            tt--;
        }
        q[++tt] = i; //将当前的位置加入到队列里
        if(hh < tt) S.insert(f[q[tt - 1]] + w[q[tt]]);// 如果当前队列的元素数量大于1,那么就把当前队列的对应的值插入集合当中
        
        f[i] = f[j - 1] + w[q[hh]]; // 这里其实也就是处理了队列里只有一个元素的情况,结果就是最后一段的前一个地址的f[j - 1]加上当前队头的值
        if(S.size()) f[i] = min(f[i], *S.begin()); // *S.begin()就是取到集合中的最小值,其实也就是当前f[i]所有方案的最小值
    }
    cout << f[n] << endl;
    return 0;
}

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

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

相关文章

MASK-RCNN网络介绍

目录前言一.MASK R-CNN网络1.1.RoIPool和RoIAlign1.2.MASK分支二.损失函数三.Mask分支预测前言 在介绍MASK R-CNN之前&#xff0c;建议先看下FPN网络&#xff0c;Faster-CNN和FCN的介绍&#xff1a;下面附上链接&#xff1a; R-CNN、Fast RCNN和Faster RCNN网络介绍FCN网络介绍…

调试射频TX和rx实验工程出现的问题与反思

1.今天用ADS仿真 发现 加上SMA 插损就到了4db&#xff0c;但是直接用传输线就在1db以内 这个问题我目前想到的排查思路是换成IPEX&#xff0c; 换成IPEX插损就变成2db 拿最新的7626去看 看到上面是SMA-3G 小针 还是结合参考的demo PCB来看 2.用射频的ipex测试LNA 发现校准…

Leetcode. 160相交链表

文章目录指针解法指针解法 核心思路 &#xff1a; 先 分别求两个链表的长度 然后长的链表先走 差距步&#xff08;长-短&#xff09; 最后长链表和短链表同时走 &#xff0c;第一地址相同的就是交点 &#xff0c;注意一定是地址相同 不可能出现上图这种情况 &#xff0c;因为C1…

开放平台订单接口

custom-自定义API操作 ​ ​​注册开通​​ taobao.custom 公共参数 名称 类型 必须 描述 key String 是 调用key&#xff08;必须以GET方式拼接在URL中&#xff09; secret String 是 调用密钥 api_name String 是 API接口名称&#xff08;包括在请求地址中&a…

【JavaSE专栏11】Java的 if 条件语句

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…

如何修改Win11上的默认程序?

在Win10之前&#xff0c;更改特定文件格式的默认程序很简单&#xff0c;但在Win11发布之后&#xff0c;很多用户都不清楚关于Win11的修改默认程序的操作步骤&#xff0c;接下来我们就一起来看看吧&#xff0c;希望可以帮助到大家。 步骤如下&#xff1a; 一、如何更改Windows 1…

第十四届蓝桥杯三月真题刷题训练——第 8 天

目录 第 1 题&#xff1a;分数 题目描述 运行限制 代码&#xff1a; 第 2 题&#xff1a;回文日期 题目描述 输入描述 输出描述 输入输出样例 运行限制 代码&#xff1a; 第 3 题&#xff1a;迷宫 代码&#xff1a; 第 1 题&#xff1a;分数 题目描述 本题为填空题…

自动写代码?别闹了!

大家好&#xff0c;我是良许。 这几天&#xff0c;GitHub 上有个很火的插件在抖音刷屏了——Copilot。 这个神器有啥用呢&#xff1f;简单来讲&#xff0c;它就是一款由人工智能打造的编程辅助工具。 我们来看看它有啥用。 首先就是代码补全功能&#xff0c;你只要给出函数…

C51---蓝牙模块---连接软件---控制LED灯

1.器件&#xff1a;C51、HC-08蓝牙模块、Ty-C数据线、杜邦线 2.软件&#xff1a;HC蓝牙助手 3.接线&#xff1a;VCC-VCC、GND-GND、RXD-TXD、TXD-RXD 4.烧写&#xff1a;STC-ISP串口助手 5.代码&#xff1a; #include "reg52.h" #include "intrins.h" …

方差分析与单因素方差分析

研究分类型自变量对数值型因变量的影响。检验统计的设定和检验方法与变量间的方差是否相等有关。 例如研究行业、服务等级对投诉数的影响&#xff1a;如表格中给出4个行业、每个行业有3个服务等级、样本容量为7、观测值为投诉数。则构成一个3维的矩阵。 在上述基础上&#xf…

iOS中Cell使用的标准附加控件(Swift版本)

Overview 概述 基类UITableViewCell的accessoryType属性可以用来控制"标准附加控件"的类型, 其声明如下: typedef NS_ENUM(NSInteger, UITableViewCellAccessoryType) {// 默认值, dont show any accessory viewUITableViewCellAccessoryNone,// regular chevron. …

[深入理解SSD系列 闪存实战2.1.7] NAND FLASH基本编程(写)操作及原理_NAND FLASH Program Operation 源码实现

前言 上面是我使用的NAND FLASH的硬件原理图,面对这些引脚,很难明白他们是什么含义, 下面先来个热身: 问1. 原理图上NAND FLASH只有数据线,怎么传输地址? 答1.在DATA0~DATA7上既传输数据,又传输地址 当ALE为高电平时传输的是地址, 问2. 从N

Keil5 开发 STM32 工程创建

1 创建新工程 打开Keil5软件&#xff0c;点Project菜单的的下来选项New uVision Project…&#xff0c;然后选择工程文件夹放置的目录&#xff0c;新建项目目录&#xff0c;如&#xff1a;LED闪烁 2 选择开发板型号 点击OK后&#xff0c;会弹出配置运行时环境的选项框&…

【碎片化知识总结】三月第一周

目录 前言 1、开发中常用的 IDEA 编辑器&#xff0c;如何做到不用每次都重新配置&#xff1f; 2、如何使用 Python 获取视频文件信息&#xff1f; 3、使用 Java 的 try-with-resources 优化代码 4、使用 shell 脚本批量修改服务器某一目录下的文件后缀名称 5、MySQL优化&…

记第一次面试的过程(C++)

说实话三月份上旬过得很充实&#xff0c;而且感觉蛮值&#xff0c;但还有不足的地方&#xff0c;今晚特地看完资料分析来复盘复盘。 时间还要回到3.2中午13.35&#xff08;别问我为什么那么准确&#xff0c;刚刚掏手机看的&#xff09;&#xff0c;我正在吃着饭看着王者荣耀的直…

Linux:epoll模式web服务器代码,代码debug

源码&#xff1a; https://blog.csdn.net/weixin_44718794/article/details/107206136 修改的地方&#xff1a; 修改后代码&#xff1a; #include <stdio.h> #include <unistd.h> #include <stdlib.h> //#include “epoll_server.h” #ifndef _EPOLL_SER…

Face Forgery Suvery

文章目录Face ForgeryFace Forgery classAttribute ManipulationExpression SwapIdentity SwapEntire Face SynthesisFace Forgery DetectionLow-levelOn the Detection of Digital Face Manipulation(CVPR2020)High-levelProtecting World Leaders Against Deep FakesDetectin…

国外SEO舆情处理最佳黄金时间

在国外市场&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;的舆情处理是非常重要的&#xff0c;因为它可以帮助提高网站的排名和流量&#xff0c;并且建立品牌的声誉和信誉。 然而&#xff0c;在什么时间进行舆情处理是一个值得探讨的问题。 在本文中&#xff0c;我们将…

【微信小程序】-- 自定义组件 - 数据监听器 - 案例 (三十五)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

elasticsearch全解 (待续)

目录elasticsearchELK技术栈Lucene与Elasticsearch关系为什么不是其他搜索技术&#xff1f;Elasticsearch核心概念Cluster&#xff1a;集群Node&#xff1a;节点Shard&#xff1a;分片Replia&#xff1a;副本全文检索倒排索引正向和倒排es的一些概念文档和字段索引和映射mysql与…