ARTS第四周打卡

前言:什么是ARTS打卡?

ARTS打卡是陈皓(网名左耳朵耗子)于2019年在专栏读者发起的打卡挑战,累计吸引4000+人参与,产出了很多优质的内容。2023年8月15日打卡重启,目的是为了聚集一波热爱技术的人,延续好奇、探索、实践、分享的精神。

Algorithm:一道算法题

题目链接:Problem - 5358
描述:尺取法求解算式值
解答:根据题目可以得出求解此问题的最佳方法是双指针。
首先,有3个关键点需要注意:

以下的3个关键点和思路来自1篇博文的解答:HDU 5358 First One(尺取法+数学)_mb5f5b1df7f1e34的技术博客_51CTO博客

在这里插入图片描述

  1. 枚举位数k,求出相应的x和y。(这里GPT提示我有1个前缀和的思想,而这句话是前缀和的逆向思维)

这里前缀和的概念放上一位大佬的讲解贴子,方便快速理解:前缀和

  1. 尺取法求解思路

需要参考相关教程,我自己用的是罗勇军老师的《算法竞赛》

最后来看看GPT的解答,顺便放上代码:

// Q: 我希望你扮演一名精通acm各类算法的高手,我将给你一段代码,你可以解释出代码的逻辑,并给我讲解这段代码用到的技巧。讲解技巧时可以拓展并举例,讲解时需要通俗的语言,可以说专业名词。我的代码是:

public class Main {
    static long[] a = new long[100010];
    static long[] sum = new long[100010];

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int T = scanner.nextInt();
        while (T-- > 0) {
            int n = scanner.nextInt();
            for (int i = 1; i <= n; i++) {
                a[i] = scanner.nextLong();
                sum[i] = sum[i - 1] + a[i];
            }
            long ans = 0;
            for (long i = 1; i <= 35; i++) {
                long l = 1, r = 0;
                long minn = 1L << (i - 1), maxx = (1L << i) - 1;
                if (i == 1)
                    minn = 0;
                for (long x = 1; x <= n; x++) {
                    l = Math.max(l, x);
                    while (l <= n && sum[(int) l] - sum[(int) (x - 1)] < minn) l++;
                    r = Math.max(r, l - 1);
                    while (r + 1 <= n && sum[(int) (r + 1)] - sum[(int) (x - 1)] <= maxx
                            && sum[(int) (r + 1)] - sum[(int) (x - 1)] >= minn)
                        r++;

                    if (l > r)
                        continue;
                    ans = ans + (x * (r - l + 1) + (r + l) * (r - l + 1) / 2) * i;
                }
            }
            System.out.println(ans);
        }
        scanner.close();
    }
}

这段代码看起来是为了解决某种特定的算法问题,它使用了Java编程语言。代码的大致逻辑是读取输入,然后计算基于这些输入的某种累加结果。下面我将逐步解释这段代码的逻辑和使用的技巧。

首先,我们看到`Practice5358`类中定义了两个静态数组`a`和`sum`。这些数组用于存储输入的数值和它们的前缀和,前缀和是一个常用的技巧,它可以快速计算任意区间的和。

接下来,`main`方法开始执行,使用`Scanner`类读取输入。`T`是测试用例的数量,程序会针对每个测试用例执行循环体内容。

在每个测试用例中,首先读取`n`,代表数组`a`的长度。然后,循环读取`n`个数值填充到数组`a`中,并同时计算前缀和存储到数组`sum`中。前缀和`sum[i]`表示从`a[1]`到`a[i]`的和,这是通过动态编程(dynamic programming)的方式逐步构建的。

接着,代码进入到一个嵌套循环中,外层循环变量`i`从135,这可能是根据问题的特定条件设置的。内层循环用于计算某种基于前缀和的累加结果`ans`。

在内层循环中,`minn`和`maxx`定义了根据变量`i`计算的某个区间,这个区间可能与问题的逻辑有关。注意到这里使用了位运算符`<<`来计算2的幂,这是一种效率很高的计算2的幂次的方法。

内层循环中的两个`while`循环是为了找到满足条件的`l`和`r`的值,这些值表示满足条件的区间。这里使用了双指针技巧,一个指针`l`向前移动直到找到满足条件的最小值,另一个指针`r`向前移动直到找到满足条件的最大值。这是一种减少不必要计算的方法,可以提高效率。

最后,内层循环中的复杂表达式计算了基于`x`、`l`、`r`和`i`的累加结果,并将其加到`ans`上。这个表达式可能是根据问题的特定逻辑设计的,用于计算某种加权和。

在所有测试用例处理完毕后,程序输出最终的累加结果`ans`。

总结一下,这段代码使用了以下技巧:

1. **前缀和数组**:用于快速计算区间和。
2. **动态编程**:递增地构建前缀和数组。
3. **位运算**:高效计算2的幂次。
4. **双指针**:在数组中快速找到满足条件的区间。

Q:你能详细解释一下双指针在这段代码中是如何运行的吗?双指针有哪些很好的使用技巧?

当然可以。在这段代码中,双指针技巧被用来高效地找到满足特定条件的连续子序列。让我们仔细看看这段代码是如何使用双指针的:

for (long x = 1; x <= n; x++) {
    l = Math.max(l, x);
    while (l <= n && sum[(int) l] - sum[(int) (x - 1)] < minn) l++;
    r = Math.max(r, l - 1);
    while (r + 1 <= n && sum[(int) (r + 1)] - sum[(int) (x - 1)] <= maxx
            && sum[(int) (r + 1)] - sum[(int) (x - 1)] >= minn)
        r++;

    if (l > r)
        continue;
    ans = ans + (x * (r - l + 1) + (r + l) * (r - l + 1) / 2) * i;
}

在这个内层循环中,`x`是起始指针,它从1遍历到`n`。对于每个`x`,我们都要找到一个区间`[l, r]`,使得`sum[r] - sum[x - 1]`的值位于`[minn, maxx]`之间。

1. **左指针`l`**:对于每个固定的`x`,左指针`l`从`x`开始或者从上一次的`l`值开始(因为`l`是非递减的),向右移动直到找到第一个使得`sum[l] - sum[x - 1] >= minn`的位置。这个过程是在寻找以`x`为起点的,满足条件的最短子序列。

2. **右指针`r`**:右指针从`l - 1`开始(或者上一次的`r`值,如果更大),向右移动直到找到最后一个使得`sum[r] - sum[x - 1] <= maxx`的位置。这个过程是在寻找以`x`为起点的,满足条件的最长子序列。

这两个指针共同定义了一个满足条件的区间,然后代码计算了这个区间内所有可能的子序列,并将它们的某种加权和累加到`ans`中。

双指针技巧的关键优势在于它的效率。在最坏的情况下,每个指针最多只会遍历整个数组一次,因此时间复杂度通常是O(n)。

双指针技巧的其他常见应用包括:

1. **有序数组的两数之和**:在有序数组中找到两个数,使得它们的和等于给定值。可以用一个指针从开始遍历,另一个指针从末尾遍历,根据当前和与目标值的比较,决定是移动左指针还是右指针。

2. **数组去重**:在有序数组中去除重复的元素。可以用一个慢指针表示处理过的不重复部分的末尾,一个快指针遍历数组,当发现新的不重复元素时,慢指针前进并更新。

3. **最长无重复字符子串**:使用两个指针维护一个滑动窗口,保证窗口内没有重复的字符,可以用来求解最长无重复字符子串的长度。

4. **链表中环的检测**:使用快慢指针的方法检测链表中是否存在环。慢指针每次移动一步,快指针每次移动两步,如果链表有环,那么快慢指针最终会相遇。

双指针技巧的关键在于理解如何根据问题的特点来移动指针,以及如何利用指针的移动来减少不必要的计算,从而提高算法的效率。

然后说一下坑:
刚开始不知道题目的思路和技巧,拿到题我看的非常懵,然后我就按普通的累加写了一版思路,但最后得到的时间复杂度是o(n3),然后开始摆烂。。。思索一番开始求助,终于知道最中间包含的式子的含义,以及题目的求解思路。

最后的感悟:多刷题,多刷题,多刷题!

Review:阅读一篇英文文章

分享一篇英文文章,是在扇贝阅读打卡的。
原文链接:http://xhslink.com/dMVb3

占个坑,明天阅读英语再更新

Tips:分享一个小技术

这周看了一点金额和转换的写法,主要看到几点:

  1. 金额的字段设计用BigDecimal类型,且精度高一些比较好
  2. 电商的金额计算一般用的是银行家算法(四舍六入五成双)
  3. 汇率的精度一般取四位小数,且不会舍与入
  4. JavaScript的tofixed方法不支持银行家算法,需手动实现
  5. Java的BigDecimal类型的对应银行家算法的是RoundingMode.HALF_EVEN参数

Share:表达一个观点:

最近参加了读书社群,把拿读书的感悟来分享一下吧。

日期:2024/01/14

阅读书籍&进度:《平凡的世界》20.8%

阅读时长:10分钟

场景1:县领导对资本主义行为的态度逐渐改变,田福军听到被批斗的人劳教时发出了“这成国民党了”的感叹。

场景2:劳教的民工被释放后,争先向田福军告状。回去的路上,田福军一行人意识到当前政策的错误。

场景3:截取书中原文:“明川,你能考虑这么重大的问题,很不简单。好!尽管我们都是些普通人,无法改变我们国家的局面,但我们应该有一双分辨黑白的眼睛,有一颗能严肃思考我们国家命运的头脑……你感觉到的问题,任何一个有头脑、有良心的中国人都会感觉到的。这不是我们几个人的忧虑,而是全中国人民的忧虑……””

场景4:截取书中原文:“锁链可以锁住门窗,锁住手脚,但人心是锁不住的——周恩来活在人们心中!”

场景5:少安即将结婚,但是无法置办婚礼,这件事让孙玉厚非常发愁。但是少安已经想到并解决了这些问题。

阅读感悟:

理性。尽管绝对理性和非此即彼的黑白是不存在的,我们仍应拥有辨识是非的慧眼和严谨思考、解决问题的头脑。

尽量审慎的查看自己当前的处境。当我看到场景3时,有一种感受:我们这一代人的观念与时代的发展有很大的关系。我作为一个个体,应该顺应时代变化,从中谋求自我发展。这让我回想起近年来的第四次工业革命,它开启了一个新纪元,在这个纪元中,计算能力和数据模型变得极为重要。我应该借助这些工具来提升自我,确保在未来几年能够跟上这个浪潮。

肖申克的救赎。我虽然不喜欢这份工作,但是现在处于经济下行时期,我需要先用自己擅长的技能保证活下去,再培养自己的硬本领,为将来的发展打好基础。
  • 38
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值