Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >2021-05-08:给定两个非负数组x和hp,长度都是N,再给定一个正数range

2021-05-08:给定两个非负数组x和hp,长度都是N,再给定一个正数range

原创
作者头像
福大大架构师每日一题
修改于 2021-05-11 02:03:25
修改于 2021-05-11 02:03:25
5050
举报

2021-05-08:给定两个非负数组x和hp,长度都是N,再给定一个正数range。x有序,xi表示i号怪兽在x轴上的位置;hpi表示i号怪兽的血量 。range表示法师如果站在x位置,用AOE技能打到的范围是: x-range,x+range,被打到的每只怪兽损失1点血量 。返回要把所有怪兽血量清空,至少需要释放多少次AOE技能?

福大大 答案2021-05-08:

1.贪心策略:永远让最左边缘以最优的方式(AOE尽可能往右扩,最让最左边缘盖住目前怪的最左)变成0,也就是选择:一定能覆盖到最左边缘, 但是尽量靠右的中心点。等到最左边缘变成0之后,再去找下一个最左边缘...

2.贪心策略加线段树,可优化成O(N * logN)的方法。

代码用golang编写。代码如下:

代码语言:txt
AI代码解释
复制
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
}

执行结果如下:

图片
图片

左神java代码

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
2021-12-17:长城守卫军问题。 长城上有连成一排的n个烽火
每个烽火台的基础战斗力为士兵数,另外,每个能影响此烽火台的将军都能使这个烽火台的战斗力提升k。
福大大架构师每日一题
2021/12/17
4410
2021-07-31:给定数组father,大小为N,表示一共有N个节
2021-07-31:给定数组father,大小为N,表示一共有N个节点,fatheri = j 表示点i的父亲是点j, father表示的树一定是一棵树而不是森林,给定数组values,大小为N,valuesi=v表示节点i的权值是v。实现如下4个方法,保证4个方法都很快!1)让某个子树所有节点值加上v,入参:int head, int v;2)查询某个子树所有节点值的累加和,入参:int head;3)在树上从a到b的整条链上所有加上v,入参:int a, int b, int v;4)查询在树上从a到b的整条链上所有节点值的累加和,入参:int a, int b。
福大大架构师每日一题
2021/07/31
2760
2021-07-31:给定数组father,大小为N,表示一共有N个节点,father[i] = j 表示点i的父亲是点j, f
2021-07-31:给定数组father,大小为N,表示一共有N个节点,father[i] = j 表示点i的父亲是点j, father表示的树一定是一棵树而不是森林,给定数组values,大小为N,values[i]=v表示节点i的权值是v。实现如下4个方法,保证4个方法都很快!1)让某个子树所有节点值加上v,入参:int head, int v;2)查询某个子树所有节点值的累加和,入参:int head;3)在树上从a到b的整条链上所有加上v,入参:int a, int b, int v;4)查询在树上从a到b的整条链上所有节点值的累加和,入参:int a, int b。
福大大架构师每日一题
2021/08/05
6700
2021-11-27:给定一个数组arr,长度为N,做出一个结构,
2021-11-27:给定一个数组arr,长度为N,做出一个结构,可以高效的做如下的查询:
福大大架构师每日一题
2021/11/27
2330
2023-06-20:给定一个长度为N的数组arr,arr[i]表示宝石的价值 你在某天遇到X价值的宝石, X价值如果是所有剩余
1.第一个方法(days1)使用了暴力的方式,通过遍历数组并移动宝石来模拟每一天的操作,直到所有宝石都被送出。时间复杂度较高。
福大大架构师每日一题
2023/07/08
3640
2023-06-20:给定一个长度为N的数组arr,arr[i]表示宝石的价值 你在某天遇到X价值的宝石, X价值如果是所有剩余
2022-03-18:arr数组长度为n, magic数组长度为m 比如 arr = { 3, 1, 4, 5, 7 },如果完全不改变arr中的值, 那么收益
比如 arr = { 3, 1, 4, 5, 7 },如果完全不改变arr中的值,
福大大架构师每日一题
2022/03/18
8150
2021-12-28:给定一个二维数组matrix,matrix[i][j] = k代
从(i,j)位置可以随意往右跳<=k步,或者从(i,j)位置可以随意往下跳<=k步,
福大大架构师每日一题
2021/12/28
2740
2023-07-20:假设一共有M个车库,编号1~M,时间点从早到晚是从1~T, 一共有N个记录,每一条记录如下{a, b, c
2023-07-20:假设一共有M个车库,编号1 ~ M,时间点从早到晚是从1 ~ T,
福大大架构师每日一题
2023/07/25
2730
2023-07-20:假设一共有M个车库,编号1~M,时间点从早到晚是从1~T, 一共有N个记录,每一条记录如下{a, b, c
2022-12-16:给你一个长度为n的数组,并询问q次 每次询问区间[l,r]之间是否存在小于等于k个数的和大于等于x 每条查询返回true或者false。
2022-12-16:给你一个长度为n的数组,并询问q次 每次询问区间l,r之间是否存在小于等于k个数的和大于等于x 每条查询返回true或者false。 1 <= n, q <= 10^5 k <= 10 1 <= x <= 10^8。 答案2022-12-16: 线段树。 代码用go语言编写。代码如下: package main import ( "fmt" "math/rand" "sort" "time" ) func main() { rand.Seed(time.Now().Uni
福大大架构师每日一题
2022/12/16
1.5K0
2022-12-16:给你一个长度为n的数组,并询问q次 每次询问区间[l,r]之间是否存在小于等于k个数的和大于等于x 每条查询返回true或者false。
2022-08-22:给定一个数组arr,长度为n,最多可以删除一个连续子数组, 求剩下的数组,严格连续递增的子数组最大长度。 n <= 10^6。 来自字节。
2022-08-22:给定一个数组arr,长度为n,最多可以删除一个连续子数组,求剩下的数组,严格连续递增的子数组最大长度。n <= 10^6。来自字节。5.6笔试。答案2022-08-22:动态规划+线段树。代码用rust编写。代码如下:use rand::Rng;fn main() { let n: i32 = 100; let v: i32 = 20; let test_time: i32 = 50000; println!("测试开始"); for _ in 0..te
福大大架构师每日一题
2022/08/22
5340
2022-08-22:给定一个数组arr,长度为n,最多可以删除一个连续子数组, 求剩下的数组,严格连续递增的子数组最大长度。 n <= 10^6。 来自字节。
2022-06-09:每个会议给定开始和结束时间, 后面的会议如果跟前面的会议有任何冲突,完全取消冲突的、之前的会议,安排当前的。 给定一个会议数组,返回安排的
2022-06-09:每个会议给定开始和结束时间,后面的会议如果跟前面的会议有任何冲突,完全取消冲突的、之前的会议,安排当前的。给定一个会议数组,返回安排的会议列表。来自通维数码。答案2022-06-09:彻底的流程模拟。线段树。代码用rust编写。代码如下:use rand::Rng;fn main() { let n: i32 = 100; let t: i32 = 5000; let test_time: i32 = 20000; println!("测试开始"); fo
福大大架构师每日一题
2022/06/09
4510
2022-06-09:每个会议给定开始和结束时间, 后面的会议如果跟前面的会议有任何冲突,完全取消冲突的、之前的会议,安排当前的。 给定一个会议数组,返回安排的
2021-04-04:给定一个非负数组arr,和一个正数m的最大值。
2021-04-04:给定一个非负数组arr,和一个正数m。 返回arr的所有子序列中累加和%m之后的最大值。
福大大架构师每日一题
2021/04/04
8850
2021-04-04:给定一个非负数组arr,和一个正数m的最大值。
2022-12-14:给定一个正数n, 表示从0位置到n-1位置每个位置放着1件衣服 从0位置到n-1位置不仅有衣服,每个位置还摆着1个机器人 给定两个长度为n
2022-12-14:给定一个正数n, 表示从0位置到n-1位置每个位置放着1件衣服
福大大架构师每日一题
2022/12/14
5120
2022-12-14:给定一个正数n, 表示从0位置到n-1位置每个位置放着1件衣服 从0位置到n-1位置不仅有衣服,每个位置还摆着1个机器人 给定两个长度为n
HDU 5877 2016大连网络赛 Weak Pair(树状数组,线段树,动态开点,启发式合并,可持久化线段树)
Weak Pair Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total Submission(s): 1468    Accepted Submission(s): 472 Problem Description You are given a  tree of  nodes, labeled from 1 to . To the th node a non-n
ShenduCC
2018/04/27
7210
HDU 4348 To the moon(可持久化线段树)
To the moon Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 4287    Accepted Submission(s): 923 Problem Description Background To The Moon is a independent game released in November 2011, it is
ShenduCC
2018/04/27
6990
2023-07-25:你驾驶出租车行驶在一条有 n 个地点的路上 这 n 个地点从近到远编号为 1 到 n ,你想要从 1 开到
每一位 你选择接单的乘客 i ,你可以 盈利 endi - starti + tipi 元
福大大架构师每日一题
2023/08/10
2630
2023-07-25:你驾驶出租车行驶在一条有 n 个地点的路上 这 n 个地点从近到远编号为 1 到 n ,你想要从 1 开到
2022-05-10:在字节跳动,大家都使用飞书的日历功能进行会议室的预订,遇到会议高峰时期, 会议室就可能不够用,现在请你实现一个算法,判断预订会议时是否有空
2022-05-10:在字节跳动,大家都使用飞书的日历功能进行会议室的预订,遇到会议高峰时期,
福大大架构师每日一题
2022/05/10
4910
2022-07-29:一共有n个人,从左到右排列,依次编号0~n-1, h[i]是第i个人的身高, v[i]是第i个人的分数, 要求从左到右选出一个子序列,在这
n <= 10的5次方, 1 <= hi <= 10的9次方, 1 <= vi <= 10的9次方。
福大大架构师每日一题
2022/07/29
3030
2022-07-29:一共有n个人,从左到右排列,依次编号0~n-1, h[i]是第i个人的身高, v[i]是第i个人的分数, 要求从左到右选出一个子序列,在这
2022-01-12:给定一个正数数组arr,长度为n,下标0~n-1, a
中间位置i需要达标,达标的条件是 : arri-1 > arri 或者 arri+1 > arri哪个都可以。
福大大架构师每日一题
2022/01/12
3440
2025-01-04:不包含相邻元素的子序列的最大和。用go语言,给定一个整数数组 nums 和一个由二维数组 queries
2025-01-04:不包含相邻元素的子序列的最大和。用go语言,给定一个整数数组 nums 和一个由二维数组 queries 组成的查询列表,其中每个查询的格式为 queries[i] = [posi, xi]。
福大大架构师每日一题
2025/01/07
970
2025-01-04:不包含相邻元素的子序列的最大和。用go语言,给定一个整数数组 nums 和一个由二维数组 queries
推荐阅读
2021-12-17:长城守卫军问题。 长城上有连成一排的n个烽火
4410
2021-07-31:给定数组father,大小为N,表示一共有N个节
2760
2021-07-31:给定数组father,大小为N,表示一共有N个节点,father[i] = j 表示点i的父亲是点j, f
6700
2021-11-27:给定一个数组arr,长度为N,做出一个结构,
2330
2023-06-20:给定一个长度为N的数组arr,arr[i]表示宝石的价值 你在某天遇到X价值的宝石, X价值如果是所有剩余
3640
2022-03-18:arr数组长度为n, magic数组长度为m 比如 arr = { 3, 1, 4, 5, 7 },如果完全不改变arr中的值, 那么收益
8150
2021-12-28:给定一个二维数组matrix,matrix[i][j] = k代
2740
2023-07-20:假设一共有M个车库,编号1~M,时间点从早到晚是从1~T, 一共有N个记录,每一条记录如下{a, b, c
2730
2022-12-16:给你一个长度为n的数组,并询问q次 每次询问区间[l,r]之间是否存在小于等于k个数的和大于等于x 每条查询返回true或者false。
1.5K0
2022-08-22:给定一个数组arr,长度为n,最多可以删除一个连续子数组, 求剩下的数组,严格连续递增的子数组最大长度。 n <= 10^6。 来自字节。
5340
2022-06-09:每个会议给定开始和结束时间, 后面的会议如果跟前面的会议有任何冲突,完全取消冲突的、之前的会议,安排当前的。 给定一个会议数组,返回安排的
4510
2021-04-04:给定一个非负数组arr,和一个正数m的最大值。
8850
2022-12-14:给定一个正数n, 表示从0位置到n-1位置每个位置放着1件衣服 从0位置到n-1位置不仅有衣服,每个位置还摆着1个机器人 给定两个长度为n
5120
HDU 5877 2016大连网络赛 Weak Pair(树状数组,线段树,动态开点,启发式合并,可持久化线段树)
7210
HDU 4348 To the moon(可持久化线段树)
6990
2023-07-25:你驾驶出租车行驶在一条有 n 个地点的路上 这 n 个地点从近到远编号为 1 到 n ,你想要从 1 开到
2630
2022-05-10:在字节跳动,大家都使用飞书的日历功能进行会议室的预订,遇到会议高峰时期, 会议室就可能不够用,现在请你实现一个算法,判断预订会议时是否有空
4910
2022-07-29:一共有n个人,从左到右排列,依次编号0~n-1, h[i]是第i个人的身高, v[i]是第i个人的分数, 要求从左到右选出一个子序列,在这
3030
2022-01-12:给定一个正数数组arr,长度为n,下标0~n-1, a
3440
2025-01-04:不包含相邻元素的子序列的最大和。用go语言,给定一个整数数组 nums 和一个由二维数组 queries
970
相关推荐
2021-12-17:长城守卫军问题。 长城上有连成一排的n个烽火
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档