洛谷 P1442 铁球落地【线性dp+线段树预处理+离散化】

原题链接:https://www.luogu.com.cn/problem/P1442

题目描述

在二维坐标系内有 n 个平台(定义平台是一条两端点纵坐标相同的开线段,开线段指线段两个端点不算做线段本身)和一个铁球,铁球如果下面没有物体,则每秒会下落一个单位长度。

球每次落到某个平台上后,游戏者可以选择水平向左或水平向右滚,球滚动速度是每秒 1 个单位长度。由于铁球的质量不太好,每次落下的高度不能超过 h。

设计一种策略,使得球尽快落到地面而不被摔碎。

假设地面高度为 0,且无限宽。球体积相对平台极小,可以看作一个质点。请注意,球滚动至平台的一个端点处即可下落,不需要滚动至下一个格子。例如下图,小球在 (9,9) 处已经开始下落。

输入格式

第一行有两个整数,分别代表平台个数 n 和最大下落高度 h。

第二行有两个整数 x,y,表示铁球起始时在坐标为 (x,y) 的位置。

第 3到第 (n+2) 行,每行三个整数,第(i+2) 行的整数 hi​,li​,ri​ 分别代表第 i 个平台的端点纵坐标 hi​ 和左右端点的横坐标li​,ri​。

输出格式

输出一行一个整数表示最小的坠落时间。

输入输出样例

输入 #1

5 3
6 10
5 2 4
9 3 9
6 7 10
2 1 5
3 8 11

输出 #1

15

输入 #2

10 156
84 139
63 22 50
79 96 100
87 77 98
60 24 53
47 1 29
62 55 89
68 68 78
10 5 85
85 67 71
73 57 61

输出 #2

155

说明/提示

数据规模与约定

对于全部的测试点,保证:

  • 1≤n≤1e5。
  • 1≤x,y,h,hi​,li​,ri​≤1e9,li​≤ri​。
  • 对于所有的 hi​,保证互不相同,li​ 与 ri​ 也互不相同,且对于任意i不等于j, 保证 li不等于rj。
  • 数据保证有解,最终答案不超过 1e9。

解题思路:

首先当球在一个线段上时,存在俩种操作。

第一种操作是从线段左边往下滚掉到下一个线段。

第二种操作是从线段右边往下滚掉到下一个线段。

所以我们要么每次往左滚,要么就是往右滚,并且不管是往左边滚还是往右边滚都是只能掉到某一个确定的线段上的,也就是说可能的情况是有限的,这个观察题目描述中给出的图就可以发现了,这明显就是线性dp的常规操作,所以我们可以考虑进行dp,并且这个题目说了所有线段的l,r,h都是不同的,所以我们可以根据高度从小到达对所有线段进行排序,由于这个球从上面掉下来最终肯定会到达地面的某一个位置,我们可以把地面作为初始位置考虑从下往上进行dp。

状态定义:

定义f[i][0]表示从第i个线段开始,并且当前第i个线段往左边滚下去并且最终到达地面的最少花费时间,f[i][1]表示从第i个线段开始,并且当前第i个线段往右边滚下去并且最终到达地面的最少花费时间。

初始状态

我们是以地面为初始状态,所以f[0][0]=f[0][1]=0

状态转移

当然如果当前线段掉下去之后是到达地面要进行特殊处理

这个状态转移里面的时间花费就是从上往下掉落高度时间的花费,和在某个线段上移动的花费,总共只有四种情况,可以自己画四个图看一下四种情况就可以列出四个表达式了,这里我就不具体描述了,这个自己画个图一眼就看出来了。

(1)当前位置往左边掉下去

  • f[i][0] = min(f[i][0], f[Left[i]][0] + a[i].stl - a[Left[i]].stl + a[i].h - a[Left[i]].h);
  • f[i][0] = min(f[i][0], f[Left[i]][1] + a[Left[i]].str - a[i].stl + a[i].h - a[Left[i]].h);

(2)当前位置往右边掉下去

  • f[i][1] = min(f[i][1], f[Right[i]][0] + a[i].str - a[Right[i]].stl + a[i].h - a[Right[i]].h);
  • f[i][1] = min(f[i][1], f[Right[i]][1] + a[Right[i]].str - a[i].str + a[i].h - a[Right[i]].h);

(3)特殊处理当前位置掉下去是地面

  • f[i][0] = a[i].h;
  • f[i][1] = a[i].h;

上述dp过程描述完了,但是dp处理里面的Left[i]和Right[i]怎么计算呢,我来画一个图描述一下

在上述图片中设线段5再往下就是地面了,线段5所在位置高度为1 ,我们是从下往上进行dp,首先计算的是线段5,线段5往下只会掉到地面特殊处理,计算完线段5之后,线段5包含横坐标区间[2,4],也就是说上面的球掉下来的位置的横坐标如果是[2,4],那么会被线段5接住,不会直接掉到地面,也就是说我们要将线段5包含区间[2,4]修改为编号5,然后处理线段4,线段4往左掉下去位置为3,刚好包含在线段5,[2,4]之间,所以线段4往左掉下去到达的线段的编号就是5,然后区间[3,7]被线段4覆盖,区间[3,7]修改为4,之前的[3,4]由编号5变为编号4,然后对于上面的所有线段都进行同样的处理即可,但是这些处理怎么进行呢,我们可以发现这些处理实际上就是俩个操作,区间修改操作和单点查询操作,这不就是线段树的经典操作吗,所以可以使用线段树进行维护,具体描述见代码处。

总结:

这个题目思路还是比较简单的,但是代码确实比较难调,没办法涉及到线段树的题目调起来就是比较麻烦,我在写这个题目时遇到了几个问题导致调了很久。

(1)易错点:首先计算线段上的位移时间时应该用原本的线段左右端点进行计算,不能用离散化后的值进行计算,我就是因为一直用的离散化后的值进行计算导致调了很长时间才调出来。

cpp代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long LL;

const int N = 1e5 + 10, M = N * 2;

int n, m;
struct Node
{
    int stl, str;   //stl,str表示的是线段初始的左右端点横坐标值
    int l, r, h;   //l,r表示的是线段离散化之后的左右端点的横坐标值
} a[N];
int startx, starty, cnt;  //(startx,starty)是球的初始坐标,cnt记录离散化之后总的点的数量
int Left[N], Right[N];   //Left[i]表示点i往左边掉下去到达的线段的标号,Right[i]表示点i从右边掉下去到达的线段的编号
int points[M];  //points数组用于离散化
struct Tree   //线段树的结构体数组
{
    int l, r, id;
    int add;
} tr[M * 4];
LL f[N][2];  //dp处理数组

void build(int u, int l, int r)  //构建线段树
{
    if (l == r)
        tr[u] = {l, r, 0, 0};
    else
    {
        tr[u] = {l, r, 0, 0};
        int mid = l + r >> 1;
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    }
}

void pushdown(int u)  //线段树的下传懒标记
{
    Tree &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
    if (root.add)
    {
        left.add = root.add, left.id = root.add;
        right.add = root.add, right.id = root.add;
        root.add = 0;
    }
}

void modify(int u, int l, int r, int id)  //线段树区间修改函数
{
    if (l <= tr[u].l && r >= tr[u].r)
    {
        tr[u].add = id;
        tr[u].id = id;
    }
    else
    {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid)
            modify(u << 1, l, r, id);
        if (r > mid)
            modify(u << 1 | 1, l, r, id);
    }
}

int query(int u, int x)  //线段树单点查询函数
{
    if (x == tr[u].l && x == tr[u].r)
        return tr[u].id;
    else
    {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (x <= mid)
            return query(u << 1, x);
        else if (x > mid)
            return query(u << 1 | 1, x);
    }
    return -1;
}
int main()
{
    cin >> n >> m;
    cin >> startx >> starty;
    n++;
    a[n] = {startx, startx, startx, startx, starty};

    //利用points将所有线段左右端点进行离散化
    points[++cnt] = startx;
    for (int i = 1; i < n; i++)
    {
        int l, r, h;
        scanf("%d%d%d", &h, &l, &r);
        a[i] = {l, r, l, r, h};
        points[++cnt] = l, points[++cnt] = r;
    }

    //对points数组排序进行离散化
    sort(points + 1, points + 1 + cnt);
    //对所有线段按照高度从小到达排序
    sort(a + 1, a + 1 + n, [&](Node A, Node B)
         { return A.h < B.h; });

    //将离散化的数据进行填充
    for (int i = 1; i <= n; i++)
    {
        a[i].l = lower_bound(points + 1, points + 1 + cnt, a[i].l) - points;
        a[i].r = lower_bound(points + 1, points + 1 + cnt, a[i].r) - points;
    }

    build(1, 1, cnt);  //初始化构建线段树
    //对当前位置往左边掉下去或者往右边掉下去会到达的线段的编号进行处理
    //线段树预处理
    for (int i = 1; i <= n; i++)
    {
        Left[i] = query(1, a[i].l);
        Right[i] = query(1, a[i].r);
        modify(1, a[i].l, a[i].r, i);
    }

    //dp处理过程
    memset(f, 0x3f, sizeof f);
    f[0][0] = f[0][1] = 0;
    for (int i = 1; i <= n; i++)
    {
        if (a[i].h - a[Left[i]].h <= m)  //当前位置往左边掉下去
        {
            if (Left[i])
            {   //花费的时间有俩部分,1.高度花费,2,在线段上移动的花费  这个花费可以看题目描述给出的图,很容易就可以看出来了这里就不仔细描述了
                f[i][0] = min(f[i][0], f[Left[i]][0] + a[i].stl - a[Left[i]].stl + a[i].h - a[Left[i]].h);
                f[i][0] = min(f[i][0], f[Left[i]][1] + a[Left[i]].str - a[i].stl + a[i].h - a[Left[i]].h);
            }
            else
                f[i][0] = a[i].h;  //到达的下一个线段是地面特殊处理
        }

        if (a[i].h - a[Right[i]].h <= m)  //当前位置往右边掉下去
        {
            if (Right[i])
            { // 花费的时间有俩部分,1.高度花费,2,在线段上移动的花费  这个花费可以看题目描述给出的图,很容易就可以看出来了这里就不仔细描述了
                f[i][1] = min(f[i][1], f[Right[i]][0] + a[i].str - a[Right[i]].stl + a[i].h - a[Right[i]].h);
                f[i][1] = min(f[i][1], f[Right[i]][1] + a[Right[i]].str - a[i].str + a[i].h - a[Right[i]].h);
            }
            else
                f[i][1] = a[i].h;   //到达的下一个位置是地面特殊处理
        }
    }

    //记录答案并输出
    LL ans = 2e18;
    for (int i = 1; i <= n; i++)
        if (a[i].stl == startx && a[i].str == startx)
        {
            ans = min(f[i][0], f[i][1]);
            break;
        }
    cout << ans << endl;
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

轻量级接口自动化测试框架

大致思路: jmeter完成接口脚本,Ant完成脚本执行并收集结果生成报告,最后利用jenkins完成脚本的自动集成运行. 环境安装: 1.jdk1.7 配置环境变量(参考前面的分页) 2.jmeter解压到本地,ant解压到本地 3.Ant解压到本地,并配置环境变量 ANT_HOME:D:\jmeter\apache-ant-1.9.6 P…

玩转Mysql 八 (MySQ优化入门篇)

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。如有侵权&#xff0c;请留言&#xff0c;我及时删除&#xff01; 前言&#xff1a; 一个高性能&#xff0c;稳定的数据库集群并不是指的某一特性优化&#xff0c;就…

嵌入式软件开发人员有必要学习系统移植的知识吗?【ppt获取见文末】

《从零开始学ARM》的配套视频说明 为了让粉丝更好的学习我的新书里面的知识&#xff0c; 一口君特地录制了配套学习视频&#xff0c; 《从0学ARM第一期》 《从0学ARM第一期》 视频已经免费发布在B站&#xff0c; 而书中除了ARM汇编、裸机开发等知识&#xff0c;还涉及到…

Java研学-过滤与监听

一 过滤器 Filter 1 介绍 Java Web 组件之一(Servlet 的功能)&#xff0c;可改变一个request和修改一个response。Filter不是Servlet&#xff0c;不能产生一个response&#xff0c;它是在一个request 到达Servlet之前预处理 request&#xff0c;也可以在response离开Servlet 后…

音频和视频基础知识

声音 什么是声音&#xff1a; 声音是由物体振动产生的&#xff0c;物体发生振动&#xff0c;对周围的空气产生挤压&#xff0c;从而产生声音。声音是一种压力波&#xff0c;使周围的空气产生疏密变化&#xff0c;形成疏密相间的纵波&#xff0c;由此产生了声波。 声波三要素&…

HTML如何设置背景图片?有几种设置背景图片的办法?

我们在编辑网页时&#xff0c;如果觉得网页过于单调&#xff0c;这时便可以加上一张自己喜欢的背景图。这篇文章中&#xff0c;W3Cschool 小编给大家介绍下 HTML 中如何设置背景图片&#xff0c;分别有哪几种设置背景图片的方法。 方法一、HTML中设置背景图片 HTML中的<bo…

5.Pytorch模型单机多GPU训练原理与实现

文章目录 Pytorch的单机多GPU训练1)多GPU训练介绍2)pytorch中使用单机多GPU训练DistributedDataParallel(DDP)相关变量及含义a)初始化b)数据准备c)模型准备d)清理e)运行 3)使用DistributedDataParallel训练模型的一个简单实例 欢迎访问个人网络日志&#x1f339;&#x1f339;知…

HTML登录页面透明样式

html <body> <form> <h4 style"text-align:center">登录中心</h4> <hr /> <br /> <div class"row mb-5"> <label class"col-sm-2 col-form-label"…

【GitHub项目推荐--国外大神复刻暗黑2】【转载】

《暗黑破坏神2》&#xff0c;由顶尖游戏公司暴雪研发&#xff0c;2000 年上市&#xff0c;其资料片 2001 年上市&#xff0c;2D 画面。相信这款游戏已经成为很多人的回忆了&#xff0c;不知道当时是不是也和我一样沉迷于收集套装呢&#xff1f; 这款游戏的剧情设计、画面感都令…

Deepin使用记录-deepin安装docker

引用 本来想在deepin中直接安装mysql的开发环境的&#xff0c;但想到还是安装docker&#xff0c;然后在docker下安装比较方便&#xff0c;所以就有了本篇文章&#xff0c;先在deepin下安装docker。 经过本次安装&#xff0c;发现在deepin下安装docker是非常的简单&#xff0c…

企业异地访问办公系统:对比运营商MPLS专线,内网穿透有何优势?

为了实现连锁门店、企业内部各地分支机构ERP、OA、远程监控、自建邮件服务器、智能网络设备等数据传输、互访&#xff0c;使用运营商专线或是采用内网穿透方案&#xff0c;彼此之间究竟有何区别呢&#xff1f; 简单来说&#xff0c;MPLS专线和普通宽带类似是运营商提供的网络租…

数学建模day15-时间序列分析

时间序列也称动态序列&#xff0c;是指将某种现象的指标数值按照时间顺序排列而成的数值序列。时间序列分析大致可分成三大部分&#xff0c;分别是描述过去、分析规律和预测未来&#xff0c;本讲将主要介绍时间序列分析中常用的三种模型&#xff1a;季节分解、指数平滑方法和AR…

20240112-剑来的小文字大道理

– 烽火戏诸侯 《剑来》 与亲近之人不要说气话&#xff0c;不要说反话&#xff0c;不要不说话。 请不要把陌生人的些许善意&#xff0c;视为珍惜的瑰宝&#xff0c;却把身边亲近人的全部付出&#xff0c;当做天经地义的事情&#xff0c;对其视而不见。 读过多少书&#xff0…

java基础知识点系列——分支语句(六)

java基础知识点系列——分支语句&#xff08;六&#xff09; 流程控制 流程控制语句分类 顺序结构分支结构循环结构 顺序结构 顺序结构是程序中最简单最基本的流程控制&#xff0c;没有特定的语法结构&#xff0c;按照代码的先后顺序&#xff0c;依次执行。 if语句 if语…

利益兑现期越短,积极性越高

在2023年一次部门项目提成时间节点的调整&#xff0c;引发了相关的销售部门 &#xff0c;项目集成部门&#xff0c;软件开发部门截然不同的工作积极性。 公司案例 公司做项目的时候&#xff0c;采用的是相关部门都可以在项目获取提成 &#xff0c;之前的提成方式为销售部门为…

openfeign服务启动成功但是注册不上nacos? 我看看怎么个事儿!

spring-cloud-starter-alibaba-nacos-discovery和spring-boot-starter-web不得不说的秘密 ! 直接上答案: 给你的服务加上springbootweb依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifa…

lv14 多路复用及信号驱动 8

1 多路复用 描述符&#xff1a; 文件描述符&#xff1a;设备文件、管道文件 socket描述符 1.1 应用层&#xff1a;三套接口select、poll、epoll select&#xff1a;位运算实现 监控的描述符数量有限&#xff08;32位机1024,64位机2048,监控对象有限&#xff09; 效率差 p…

【MATLAB】VMD_LSTM神经网络时序预测算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 变分模态分解&#xff08;Variational Mode Decomposition&#xff0c;VMD&#xff09;和LSTM&#xff08;Long Short-Term Memory&#xff09;神经网络结合的算法是一种用于处理时间序列…

国家注册信息安全专业人员十五类CISP证书

国家注册信息安全专业人员&#xff08;Certified Information Security Professiona&#xff0c;简称CISP&#xff09;&#xff0c;是面向党政机关、关键信息基础设施运营单位、各类企事业单位和社会组织以及网络与信息安全企业、测评和咨询服务机构等工作的信息安全人员颁发的…

ELK之Filebeat安装配置及日志抓取

一、Filebeat是什么 轻量型日志采集器 无论您是从安全设备、云、容器、主机还是 OT 进行数据收集,Filebeat 都将为您提供一种轻量型方法,用于转发和汇总日志与文件,让简单的事情不再繁杂。 Filebeat 随附可观测性和安全数据源模块,这些模块简化了常见格式的日志的收集、解…
最新文章