美文网首页
Kotlin练习 B. Integers Shop

Kotlin练习 B. Integers Shop

作者: 九风特 | 来源:发表于2022-01-25 15:43 被阅读0次

    理解题目
    这个题目一大障碍应该是理解题目,毕竟咱们的老妈不说英语。。。
    题目的意思是这样的:
    假设我们有一个整数商店,这个商店的商品是一个个的整数区间(线段), 比如线段[2,4]代表打包出售数字[2, 3, 4], 同理[7,8]代表打包出售[7,8], 每个线段都一个价格C, 同时商店推出优惠活动,你购买了[2,4]和[7,8], 那么你现在拥有了数字[2, 3, 4, 7, 8] 商店会赠送你礼物数字5, 和6 赠送规则如下:
    没有买x。
    买了一个小于x的整数l。
    买了大于x的整数r。
    说白了就是中间缺的都会免费给你补上形成新的连续线段。
    那么做为顾客,我们的目标是:

    1. 首先保证买到尽可能多的数字
    2. 在保证1的情况下花尽可能少的钱

    但现在商店由于技术原因, 上架的n个线段只有前s个可供出售
    所以说顾客只能在前s个线段中选择购买哪些

    现在练习编码意图如下:
    输入支持:
    第一行输入一个数字 t 表明你要进行几次试验
    然后就是进行t次试验的数据输入,其中每次试验需要输入如下数据
    第一行输入商店总共有多少条线段出售n
    然后循环输入每一条线段的参数 l, r, c一行代表一个线段(l起点, r终点, c售价)
    注意,题目中对参数有值域限定,编程中应有所体现
    讲道理我们大多数时候都是在做各种限定,各种检测,异常处理,真正的算法其实没多少东西,甚至说很简单。
    值域限定容易理解,虽然写起来挺啰嗦的,其中一条我理解的未必准确
    It is guaranteed that the total sum of n over all test cases doesn't exceed 2⋅10^5
    这是英文,我理解的是所有输入的n的总和不超过2*10^5(实例代码有体现)

    输出要求:
    前边我们说了,在n个待售线段中由于技术原因只有前s个可供出售
    那么输出方面就是让你分别设定这个s 从1-n, 然后给出在当前s值下,用户需要消费多少钱买到最多的数字
    举例来说 假设当前商店出售的n=2个线段 分别为[1,4] 20块钱 [7,8] 30块钱,那么你应该输出
    20//s=1时能买到1,2, 3, 4
    50//s=2时能买到1, 2, 3, 4...8
    好了题目说清楚了,再说说算法
    首先我们必须在不考虑价格情况下找出能买到的最多的数字,那就找到s个线段中最小和最大的数字
    有了这两个数字,根据贪心原理 在找到拥有最小数字最便宜的线段 和拥有最大数字最便宜的线段,这俩个价格相加即为我们最终消费(如果是同一个线段则无需加了)
    那么算法和环境输入输出条件都有了, 剩下就是具体编码练习了,本例输出是固定的,所以错就是错,对就是对.
    有兴趣的同学可以和c代码比较下,看看哪个更简洁明了
    具体编码

    import kotlin.math.pow
    
    /**
     * Accomplished using the EduTools plugin by JetBrains https://plugins.jetbrains.com/plugin/10081-edutools
     *
     * To modify the template, go to Preferences -> Editor -> File and Code Templates -> Other
     */
    /**
     * 线段数据类
     * 并且为了方便写了一个扩展函数
     */
    data class Segment(val l:Long, val r:Long, val c:Long){
        fun checkValid(){
            val range = 1..1E9.toLong()
            check(l in range)
            check(r in range)
            check(c in range)
            check(r >= l)
        }
    }
    fun List<Long>.toSegment()= run {
        check(this.size==3)
        Segment(this[0], this[1], this[2]).also {it.checkValid()}
    }
    
    /**
     * 读取一个线段类
     */
    fun readSegment() = readLine()!!
        .split(' ')
        .map { it.toLong() }
        .toSegment()
    fun main() {
        // Write your solution here
        // 输入支持
        // 输入实验次数
        val t = readLine()!!.toInt()
        check(t in 1..1_000)//值域检测
    
        //循环读取各个实验的数据
        val inputs = (1..t).map {
            val n = readLine()!!.toLong()
            check(n in 1..1E5.toLong())
            //循环读取线段数据
            (1..n).map {
                readSegment()
            }
        }
        //n 的总是不能超过2*10^5检测
        check(inputs.sumOf { it.size }<=2E5)
    
        // 上面这一堆代码实际上都是在满足练习题的输入要求
        // 包括各种检测,具体算法其实简单的一B
        for(segments in inputs){
            (1..segments.size).map {s->
                segments.subList(0, s).run {
                    //找出最小和最大的整数
                    val minInt = minOf { it.l }
                    val maxInt = maxOf { it.r }
                    //找到拥有最小整数并且售价最低的segment
                    val segWithMin = this.filter { it.l == minInt }
                        .minByOrNull { it.c }
    
                    //同理找到max
                    val segWithMax = this.filter { it.r == maxInt }
                        .minByOrNull { it.c }
                    
                    //输出结果
                    if(segWithMax!!.l != minInt && segWithMax != segWithMin){
                        println(segWithMax!!.c + segWithMin!!.c)
                    }
                    else{
                        println(segWithMax!!.c)
                    }
                }
            }
    
        }
    
    }
    

    如果确实阅读了这个代码,我们会发现算法的代码就那么几行,剩下的全是输入输出和各种检测。 虽然这是练习题,但我看这很符合实际开发的情况,当然实际开发中会更复杂一些, 现在仅仅是调用了个check函数而已,实际肯定要做处理的。

    和前边一样, 那我们在思考一下 上述代码还有没有什么好的改进意见?
    很遗憾,本人没想到太好的优化策略,最多也就是多加个条件判断提前退一下循环。意义不大。

    相关文章

      网友评论

          本文标题:Kotlin练习 B. Integers Shop

          本文链接:https://www.haomeiwen.com/subject/ogrwhrtx.html