zoukankan      html  css  js  c++  java
  • LeetCode c++--1551. 使数组中所有元素相等的最小操作数

    题目描述:

    存在一个长度为 n 的数组 arr ,其中 arr[i] = (2 * i) + 1 ( 0 <= i < n )。

    一次操作中,你可以选出两个下标,记作 x 和 y ( 0 <= x, y < n )并使 arr[x] 减去 1 、arr[y] 加上 1 (即 arr[x] -=1 且 arr[y] += 1 )。最终的目标是使数组中的所有元素都 相等 。题目测试用例将会 保证 :在执行若干步操作后,数组中的所有元素最终可以全部相等。

    给你一个整数 n,即数组的长度。请你返回使数组 arr 中所有元素相等所需的 最小操作数 。

    示例 1:

    输入:n = 3
    输出:2
    解释:arr = [1, 3, 5]
    第一次操作选出 x = 2 和 y = 0,使数组变为 [2, 3, 4]
    第二次操作继续选出 x = 2 和 y = 0,数组将会变成 [3, 3, 3]
    示例 2:

    输入:n = 6
    输出:9
     

    提示:

    1 <= n <= 10^4

    解题:

    1.直接计算

    class Solution {
    public:
        /*
            目的:将数组中的数都变为中位数需要进行交换的次数
            当 n :
                1.n == 1:cout<<0;
                2.为奇数时,从(n-1)/2向外扩展 i++,j--,累加到 l = (arr[i] - arr[j])/2;
                3.为偶数时,从中间 i = n / 2; j = ( n - 2 ) / 2; 累加到 l = (arr[i] - arr[j])/2;
       */
        int minOperations(int n) {
            if(n == 1)return 0;
            vector<int> arr(n,0); //创建arr空间为n,并初始化为0
            unsigned int i,l = 0,j;
            //将arr[i] == (2*i)+1进行保存
            for(i = 0; i < n ; i ++)
            {
                arr[i] = ( 2 * i ) + 1 ;  
            }
            //奇数操作
            if(n % 2 == 1)
                for(i = ( n-1 )/2, j = i; i < n ; i++ , j--)//j 到达 0 的同时 i 到达 n-1
                {
                    l += (arr[i]-arr[j])/2;
                }
            else      //偶数操作
                for(i = n/2,j = (n-2)/2; i < n ; i++ , j--)
                    {
                        l += (arr[i]-arr[j])/2;
                        //cout <<l<<" " ;
                     }
            return l;
        }
    };

    时间复杂度o(n),空间复杂度o(1)

    2.

    class Solution {
    public:
        int minOperations(int n) {
        //初始化
            vector<int> a(n);
            int sum = 0;
            for (int i = 0; i < n; ++i)
            { //计算数据
                a[i] = 2*i+1;
            //计算总和
                sum += a[i];
            }
            //计算平均
            sum /= n;
            int ans = 0;
            for (int i = 0; i < n; ++i)
            {
            //计算操作次数x2
                ans += abs(sum-a[i]);
            }
            return ans/2;
        }
    };        

    时间复杂度o(n),空间复杂度o(1)

    3.取巧,从数据中找规律

    (本方法借鉴于力扣)作者:zyoung1024
    链接:https://leetcode-cn.com/problems/minimum-operations-to-make-array-equal/solution/deng-chai-shu-lie-mo-ni-he-shu-xue-fang-fa-xiang-j/

    (1)

    拿到这道题,感觉有点绕,仔细分析发现arr[i] = (2 * i) + 1 (0 <= i < n)是典型的等差数列(1,3,5,7,9...).
    根据等差数列的求和公式,很容易求出数组arr的元素总和是n^2.
    题设中说每次操作选出两个下标x y使arr[x]减一arr[y]加一.换句话说,无论怎样选择x y,无论操作多少次,数组的总和不会变.
    题设又保证数组中所有元素最终可以全部相等.
    那我们假设最终所有元素等于a那么n*a == n^2,所以a == n,也就是说最终数组元素都是n.其实n是数组的平均值.知道最终元素都是n后,通过从数组起始和末尾下标开始向中间遍历,就可以到达操作数最小.
    假设左边的下标是i ((2 * i) + 1 < n)那么相应右边的下标是n - i.相应两个元素值与n的差都是n - 1 + 2 * i.所以我们只要计算数组中值小于n的元素与n的差的总和,就得到最小操作数了.

     int minOperations(int n) {
        int operation = 0;
        for(int i = 1; i < n ; i += 2) {
            operation += (n - i);
        }
        return operation;
    }
    // 时间复杂度是O(n) 空间复杂度是O(1)

    (2)

    因为是等差数列,很可能找到一个数学公式,用O(1)的时间复杂度解决.
    先举几个简单的例子找找规律

    n=3 最小操作数是 2
    n=4 最小操作数是 1 + 2
    n=5 最小操作数是 2 + 4
    n=6 最小操作数是 1 + 3 + 5
    n=7 最小操作数是 2 + 4 + 6
    果然有规律:
    当n是偶数的时候,最小操作数是1 + 3 + 5 + ... + n-1 = n*n/4
    当n是奇数的时候,最小操作数是2 + 4 + ... + n-1 = (n*n - 1) / 4

    注意: 上面的求和公式都是数学形式

    那能不能再简单一点呢? 如果用整除代替数学中的除法,可以将(n*n - 1) / 4修改成n*n/4,因为1整除4为0不影响最后的结果.
    所以有了下面的代码,是不是很酷 :)

     int minOperations(int n) {
         reutrn n * n / 4;
    }
    // 时空复杂度都是 O(1)



  • 相关阅读:
    JqGrid在IE8中表头不能分组的解决办法
    Task 异步小技巧
    封装好的socket,拿去用
    反射 实现不同模型相同属性赋值 第二集(automapper)
    .net破解二(修改dll)
    .net破解一(反编译,反混淆-剥壳)
    c/s 自动升级(WebService)
    反射实现不同模型相同属性赋值
    row_number() over()分页查询
    SQL函数
  • 原文地址:https://www.cnblogs.com/pipi-rtq/p/13516977.html
Copyright © 2011-2022 走看看