• 利用数学方法进行算法优化


    LeetCode 1630

    题目链接:https://leetcode.com/problems/arithmetic-subarrays/

    若一个数组中的元素按某种顺序排列后能形成等差数列,则该数组是“arithmetic”的。现在给出数组a以及若干个询问,每个询问包含两个数i和j,问a[i]至a[j]这一段子数组是否为“arithmetic”的。

    判断数组中元素能否构成等差数列,最简单的想法就是排序,排序后查看相邻两元素的差是否相等。该方法需要的时间复杂度为 O ( N × l o g N ) O(N \times log N) O(N×logN)

    实际上,可以通过等差数列通项公式将时间复杂度降至 O ( N ) O(N) O(N)。在得知项数(即N)的情况下,找出数组中的最大值max和最小值min,即可计算得到公差 d = m a x − m i n N d = \frac{max-min}{N} d=Nmaxmin。然后,对于数组中的任一元素k,若k能作为等差数列的一部分,则它在等差数列中是第 k − m i n d + 1 \frac{k-min}{d}+1 dkmin+1项。因此,我们只需要一个额外的bool数组,来判断等差数列中的每一项是否在数组中均有元素对应即可。

    bool GetAns(int* a, int lef, int rig) {
        int min = a[lef], max = a[rig];
    
        for (int i=lef; i<=rig; i++) {
            min = a[i] < min ? a[i] : min;
            max = a[i] > max ? a[i] : max;
        }
        
        if (min==max)
            return 1;
        if ((max-min)%(rig-lef)!=0) 
        //由于数组为int型,若计算得到的公差为小数的话,则绝对不可能构成等差数列
            return 0;
    
        int d = (max-min)/(rig-lef);
        bool f[505];
        memset(f, 0, sizeof(f));
        for (int i=lef; i<=rig; i++) {
            if ((a[i]-min)%d != 0)
                return 0;
            if (f[(a[i]-min)/d])
                return 0;
            f[(a[i]-min)/d] = 1;
        }
        return 1;
    }
    
    bool* checkArithmeticSubarrays(int* a, int n, int* l, int lSize, int* r, int rSize, int* returnSize){
        *returnSize = lSize;
        bool *ans = (bool*)malloc(lSize*sizeof(bool));
    
        for (int i=0; i<lSize; i++)
            ans[i] = GetAns(a, l[i], r[i]);
    
        return ans;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36



    LeetCode 1823

    题目链接: https://leetcode.com/problems/find-the-winner-of-the-circular-game/

    经典的约瑟夫环问题。n个人围成一个圈报数,从编号为1的人开始报数,报到k的人退出,然后从退出者的下一位开始从1继续报数,重复以上过程直到只剩一人,求此人(即胜利者)坐标。

    首先定义函数 f ( n , k ) f(n,k) f(n,k),表示n个人参与游戏,报数的数值为k,最后胜利者的坐标。显然, f ( 1 , k ) f(1, k) f(1,k)恒等于1。

    接下来,我们看如下这张表,假设k=3,第一行是每个人的原始坐标,下面的每行都是每轮报数中每个人报的数(或者说,是每个人在每轮的临时坐标):

    请添加图片描述

    第一轮,每个人报的数(也就是此人在第一轮的临时坐标)都和自己的原始坐标是一致的,第k个人报的数是k,被淘汰了,那么原来的第k+1个人以及其后面的人,在第二轮的临时坐标又从1开始计数,因此在第二轮中每个人的临时坐标都比第一轮少k。同理,第三轮每个人的临时坐标要比第二轮的少k。实际上,由于环的存在,假设某人第i轮的临时坐标为 p i p_i pi,有 p i = ( p i + 1 + k ) % n i p_i = (p_{i+1} + k)\% n_i pi=(pi+1+k)%ni,其中 n i n_i ni表示参与第i轮的总人数。

    另外,对于 f ( n , k ) f(n,k) f(n,k)来说, f ( n , k ) f(n, k) f(n,k)得到的是胜利者在第一轮的临时坐标(其实就是原坐标), f ( n − 1 , k ) f(n-1, k) f(n1,k)得到的是胜利者在第二轮的临时坐标…… f ( n − x , k ) f(n - x, k) f(nx,k)得到的是胜利者在x+1轮的临时坐标 p x + 1 p_{x+1} px+1

    由此可得 f ( n , k ) = ( f ( n − 1 , k ) + k ) % n f(n, k) = (f(n-1, k) + k) \% n f(n,k)=(f(n1,k)+k)%n。实际上,该公式计算出来可能得到结果为0的情况,这是因为胜利者在第i轮的坐标 p i = n i p_i = n_i pi=ni,此时该坐标对 n i n_i ni取模会得到0,因此:
    f ( n , k ) = { ( f ( n − 1 , k ) + k ) % n ( f ( n − 1 , k ) + k ) % n > 0 n ( f ( n − 1 , k ) + k ) % n = 0 f(n, k) =

    {(f(n1,k)+k)%n(f(n1,k)+k)%n>0n(f(n1,k)+k)%n=0" role="presentation" style="position: relative;">{(f(n1,k)+k)%n(f(n1,k)+k)%n>0n(f(n1,k)+k)%n=0
    f(n,k)={(f(n1,k)+k)%nn(f(n1,k)+k)%n>0(f(n1,k)+k)%n=0

    int findTheWinner(int n, int k){
        int ans = 1;
        for (int i=2; i<=n; i++) 
            if ((ans + k) % i == 0)
                ans = i;
            else 
                ans = (ans + k) % i;
        return ans;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9



    参考资料

    约瑟夫环——公式法(递推公式)

  • 相关阅读:
    Web APIs 第03天上
    算法模型总结:哈希
    建木×GitLink,解锁高效开发新体验
    区块链(4):java区块链项目前言
    计算机网络【CN】TCP报文段格式【20B】
    HTML+CSS大作业:众志成城 抗击疫情 抗击疫情网页制作作业 疫情防控网页设计
    前端网页打开本地应用程序
    「五度情报站」网罗全量企业情报,找客户、查竞品、寻商机!
    Hudi Spark源码学习总结-spark.read.format(“hudi“).load
    六个步骤搞定学术论文写作!
  • 原文地址:https://blog.csdn.net/Zerg_Wang/article/details/126820922