2021-05-08:给定两个非负数组x和hp,长度都是N,再给定一个正数range。x有序,x[i]表示i号怪兽在x轴上的位置;hp[i]表示i号怪兽的血量 。range表示法师如果站在x位置,用A
2021-05-08:给定两个非负数组x和hp,长度都是N,再给定一个正数range。x有序,x[i]表示i号怪兽在x轴上的位置;hp[i]表示i号怪兽的血量 。range表示法师如果站在x位置,用AOE技能打到的范围是: [x-range,x+range],被打到的每只怪兽损失1点血量 。返回要把所有怪兽血量清空,至少需要释放多少次AOE技能?
福大大 答案2021-05-08:
1.贪心策略:永远让最左边缘以最优的方式(AOE尽可能往右扩,最让最左边缘盖住目前怪的最左)变成0,也就是选择:一定能覆盖到最左边缘, 但是尽量靠右的中心点。等到最左边缘变成0之后,再去找下一个最左边缘…
2.贪心策略加线段树,可优化成O(N * logN)的方法。
代码用golang编写。代码如下:
package main
import "fmt"
func main() {
if true {
x := []int{0, 1, 2, 3, 4, 5, 6}
hp := []int{6, 4, 2, 9, 3, 6, 4}
range2 := 4
ret := minAoe1(x, hp, range2)
fmt.Println(ret)
}
if true {
x := []int{0, 1, 2, 3, 4, 5, 6}
hp := []int{6, 4, 2, 9, 3, 6, 4}
range2 := 4
ret := minAoe2(x, hp, range2)
fmt.Println(ret)
}
}
// 贪心策略:永远让最左边缘以最优的方式(AOE尽可能往右扩,最让最左边缘盖住目前怪的最左)变成0,也就是选择:
// 一定能覆盖到最左边缘, 但是尽量靠右的中心点
// 等到最左边缘变成0之后,再去找下一个最左边缘...
func minAoe1(x []int, hp []int, range2 int) int {
N := len(x)
ans := 0
for i := 0; i < N; i++ {
if hp[i] > 0 {
triggerPost := i
for triggerPost < N && x[triggerPost]-x[i] <= range2 {
triggerPost++
}
ans += hp[i]
aoe(x, hp, i, triggerPost-1, range2)
}
}
return ans
}
func aoe(x []int, hp []int, L int, trigger int, range2 int) {
N := len(x)
RPost := trigger
for RPost < N && x[RPost]-x[trigger] <= range2 {
RPost++
}
minus := hp[L]
for i := L; i < RPost; i++ {
hp[i] = getMax(0, hp[i]-minus)
}
}
func getMax(a int, b int) int {
if a > b {
return a
} else {
return b
}
}
// 贪心策略和方法二一样,但是需要用线段树,可优化成O(N * logN)的方法,
func minAoe2(x []int, hp []int, range2 int) int {
N := len(x)
// coverLeft[i]:如果以i为中心点放技能,左侧能影响到哪,下标从1开始,不从0开始
// coverRight[i]:如果以i为中心点放技能,右侧能影响到哪,下标从1开始,不从0开始
coverLeft := make([]int, N+1)
coverRight := make([]int, N+1)
left := 0
right := 0
for i := 0; i < N; i++ {
for x[i]-x[left] > range2 {
left++
}
for right < N && x[right]-x[i] <= range2 {
right++
}
coverLeft[i+1] = left + 1
coverRight[i+1] = right
}
// best[i]: 如果i是最左边缘点,选哪个点做技能中心点最好,下标从1开始,不从0开始
best := make([]int, N+1)
trigger := 0
for i := 0; i < N; i++ {
for trigger < N && x[trigger]-x[i] <= range2 {
trigger++
}
best[i+1] = trigger
}
st := NewSegmentTree(hp)
st.build(1, N, 1)
ans := 0
for i := 1; i <= N; i++ {
leftEdge := st.query(i, i, 1, N, 1)
if leftEdge > 0 {
ans += leftEdge
t := best[i]
l := coverLeft[t]
r := coverRight[t]
st.add(l, r, (int)(-leftEdge), 1, N, 1)
}
}
return ans
}
type SegmentTree struct {
// arr[]为原序列的信息从0开始,但在arr里是从1开始的
// sum[]模拟线段树维护区间和
// lazy[]为累加懒惰标记
// change[]为更新的值
// update[]为更新慵懒标记
MAXN int
arr []int
sum []int
lazy []int
change2 []int
update2 []bool
}
func NewSegmentTree(origin []int) *SegmentTree {
ret := &SegmentTree{}
MAXN := len(origin) + 1
ret.arr = make([]int, MAXN) // arr[0] 不用 从1开始使用
for i := 1; i < MAXN; i++ {
ret.arr[i] = origin[i-1]
}
ret.sum = make([]int, MAXN<<2) // 用来支持脑补概念中,某一个范围的累加和信息
ret.lazy = make([]int, MAXN<<2) // 用来支持脑补概念中,某一个范围沒有往下傳遞的纍加任務
ret.change2 = make([]int, MAXN<<2) // 用来支持脑补概念中,某一个范围有没有更新操作的任务
ret.update2 = make([]bool, MAXN<<2) // 用来支持脑补概念中,某一个范围更新任务,更新成了什么
return ret
}
func (this *SegmentTree) pushUp(rt int) {
this.sum[rt] = this.sum[rt<<1] + this.sum[rt<<1|1]
}
// 之前的,所有懒增加,和懒更新,从父范围,发给左右两个子范围
// 分发策略是什么
// ln表示左子树元素结点个数,rn表示右子树结点个数
func (this *SegmentTree) pushDown(rt int, ln int, rn int) {
if this.update2[rt] {
this.update2[rt<<1] = true
this.update2[rt<<1|1] = true
this.change2[rt<<1] = this.change2[rt]
this.change2[rt<<1|1] = this.change2[rt]
this.lazy[rt<<1] = 0
this.lazy[rt<<1|1] = 0
this.sum[rt<<1] = this.change2[rt] * ln
this.sum[(rt<<1)|1] = this.change2[rt] * rn
this.update2[rt] = false
}
if this.lazy[rt] != 0 {
this.lazy[rt<<1] += this.lazy[rt]
this.sum[rt<<1] += this.lazy[rt] * ln
this.lazy[(rt<<1)|1] += this.lazy[rt]
this.sum[(rt<<1)|1] += this.lazy[rt] * rn
this.lazy[rt] = 0
}
}
// 在初始化阶段,先把sum数组,填好
// 在arr[l~r]范围上,去build,1~N,
// rt : 这个范围在sum中的下标
func (this *SegmentTree) build(l int, r int, rt int) {
if l == r {
this.sum[rt] = this.arr[l]
return
}
mid := (l + r) >> 1
this.build(l, mid, rt<<1)
this.build(mid+1, r, rt<<1|1)
this.pushUp(rt)
}
func (this *SegmentTree) update(L int, R int, C int, l int, r int, rt int) {
if L <= l && r <= R {
this.update2[rt] = true
this.change2[rt] = C
this.sum[rt] = C * (r - l + 1)
this.lazy[rt] = 0
return
}
// 当前任务躲不掉,无法懒更新,要往下发
mid := (l + r) >> 1
this.pushDown(rt, mid-l+1, r-mid)
if L <= mid {
this.update(L, R, C, l, mid, rt<<1)
}
if R > mid {
this.update(L, R, C, mid+1, r, rt<<1|1)
}
this.pushUp(rt)
}
// L..R -> 任务范围 ,所有的值累加上C
// l,r -> 表达的范围
// rt 去哪找l,r范围上的信息
func (this *SegmentTree) add(L int, R int, C int, l int, r int, rt int) {
// 任务的范围彻底覆盖了,当前表达的范围
if L <= l && r <= R {
this.sum[rt] += C * (r - l + 1)
this.lazy[rt] += C
return
}
// 任务并没有把l...r全包住
// 要把当前任务往下发
// 任务 L, R 没有把本身表达范围 l,r 彻底包住
mid := (l + r) >> 1 // l..mid (rt << 1) mid+1...r(rt << 1 | 1)
// 下发之前所有攒的懒任务
this.pushDown(rt, mid-l+1, r-mid)
// 左孩子是否需要接到任务
if L <= mid {
this.add(L, R, C, l, mid, rt<<1)
}
// 右孩子是否需要接到任务
if R > mid {
this.add(L, R, C, mid+1, r, rt<<1|1)
}
// 左右孩子做完任务后,我更新我的sum信息
this.pushUp(rt)
}
// 1~6 累加和是多少? 1~8 rt
func (this *SegmentTree) query(L int, R int, l int, r int, rt int) int {
if L <= l && r <= R {
return this.sum[rt]
}
mid := (l + r) >> 1
this.pushDown(rt, mid-l+1, r-mid)
ans := 0
if L <= mid {
ans += this.query(L, R, l, mid, rt<<1)
}
if R > mid {
ans += this.query(L, R, mid+1, r, rt<<1|1)
}
return ans
}
执行结果如下:
2021-05-08:给定两个非负数组x和hp,长度都是N,再给定一个正数range。x有序,x[i]表示i号怪兽在x轴上的位置;hp[i]表示i号怪兽的血量 。range表示法师如果站在x位置,用A的更多相关文章
- input验证码框,输入非数字或非12位时,红框提示;每4位加一个空格
以下代码:input验证码框,输入非数字或非12位时,红框提示;每4位加一个空格 //input验证码框,输入非数字或非12位时,红框提示;每3位加一个空格 $(".text"). ...
- 非负权值有向图上的单源最短路径算法之Dijkstra算法
问题的提法是:给定一个没有负权值的有向图和其中一个点src作为源点(source),求从点src到其余个点的最短路径及路径长度.求解该问题的算法一般为Dijkstra算法. 假设图顶点个数为n,则针对 ...
- [饭后算法系列] 数组中"和非负"的最长子数组
1. 问题 给定一列数字数组 a[n], 求这个数组中最长的 "和>=0" 的子数组. (注: "子数组"表示下标必须是连续的. 另一个概念"子 ...
- KL散度非负性证明
1 KL散度 KL散度(Kullback–Leibler divergence) 定义如下: $D_{K L}=\sum\limits_{i=1}^{n} P\left(x_{i}\right) \t ...
- 2021.12.08 [SHOI2009]会场预约(平衡树游码表)
2021.12.08 [SHOI2009]会场预约(平衡树游码表) https://www.luogu.com.cn/problem/P2161 题意: 你需要维护一个 在数轴上的线段 的集合 \(S ...
- 2021.12.08 平衡树——FHQ Treap
2021.12.08 平衡树--FHQ Treap http://www.yhzq-blog.cc/fhqtreapzongjie/ https://www.cnblogs.com/zwfymqz/p ...
- 2021.05.14 tarjan
2021.05.14 tarjan 标准版tarjan 这里使用数组来模拟栈 void tarjan(int x){ ++ind; dfn[x]=low[x]=ind; stacki[++top]=x ...
- [实变函数]5.2 非负简单函数的 Lebesgue 积分
1 设 $$\bex \phi(x)=\sum_{i=1}^j c_i\chi_{E_i}(x),\quad c_i\geq 0, \eex$$ 其中 ...
- [实变函数]5.3 非负可测函数的 Lebesgue 积分
本节中, 设 $f,g,f_i$ 是可测集 $E$ 上的非负可测函数, $A,B$ 是 $E$ 的可测子集. 1 定义: (1) $f$ 在 $E$ 上的 Lebesgue 积分 ...
- 【转】17种常用的JS正则表达式 非负浮点数 非负正数.
<input type='text' id='SYS_PAGE_JumpPage' name='SYS_PAGE_JumpPage' size='3' maxlength='5' onkeyup ...
随机推荐
- 第八次团队作业:Beta冲刺(凡事预则立)
这个作业属于哪个课程 2018级计算机和综合实验班 这个作业要求在哪里 Beta冲刺要求 这个作业的目标 Beta冲刺 改进完善的功能 完善之前年度报告功能 尽快上线小程序 增加的新功能 小程序打卡功 ...
- Git 仓库7K stars!学Java开源项目austin要多久?
我是3y,一年CRUD经验用十年的markdown程序员常年被誉为职业八股文选手 开源项目消息推送平台austin仓库地址: 消息推送平台推送下发[邮件][短信][微信服务号][微信小程序][企业微 ...
- sql server连接的基本使用(包含自身验证和连接Navicat)
自身验证的步骤 1.先默认使用Windows身份验证连接上SQL Server数据库 2.右键连接名称,选择属性 来到这个界面: 3.选中左侧的安全性 4.将服务器身份验证修改为SQL Server和 ...
- Go语言 :使用简单的 for 迭代语句进行 TDD 驱动测试开发与 benchmark 基准测试
前提准备与运行环境请参考:(新手向)在Linux中使用VScode编写 "Hello,world"程序,并编写测试-Ubuntu20.4 在 Go 中 for 用来循环和迭代, ...
- 使用 GVM 搭建可维护的 Golang 开发环境
当你想完成 Golang 开发环境的便捷安装以及随时更新和保障多个版本的 Golang 共存的时候,就需要使用到 Golang 的专门版本管理工具 --gvm 本篇随笔记录了在 Ubuntu 下安装使 ...
- return、break与continue的区别
前言 在上一篇文章中,壹哥给大家介绍了while.do-while两种循环结构,并且给大家总结了两种循环的区别.实际上,我们在利用循环执行重复操作的过程中,还存在着另一个需求:如何中止,或者说提前结束 ...
- GOROOT、GOPATH、Go Modules 三者的关系介绍
GOROOT GOROOT路径即为存放Golang语言内建的程序库的所在位置,简单地说就是Golang的安装路径 若按照Folang-Download and install流程,则由go env命令 ...
- Kafka存储内幕详解
1.概述 随着微服务和分布式计算的出现,Kafka已经成为各种主流平台系统架构中不可缺少的组成部分了.在本篇文章中,笔者将尝试为大家来解密Kafka的内部存储机制是如何运作的. 2.内容 在分布式系统 ...
- LabVIEW Actor Framwork (2)________ 边学边做server&client
回顾下初始需求: 现在要做一个类似聊天的demo,一个server端,若干个client端:首先是server启动,通过server可以打开若干个client端,然后每个client可以独立给serv ...
- flask-wtf使用
Web应用程序的一个重要方面是为用户提供一个用户界面.HTML提供了一个 标签,用于设计一个接口.一个Form 元素,例如文本输入,单选框等可以适当地使用. 通过GET或POST方法将用户输入的数据以 ...