LeetCode-453. Minimum Moves to Equal Array Elements

Description

这里写图片描述

Example

这里写图片描述

Solution 1(C++)

class Solution {
public:
    int minMoves(vector<int>& nums) {
        int minnums=INT_MAX;
        int res=0;
        for(auto n: nums){
            minnums= min(minnums, n);
        }
        for(auto n: nums){
            res+= (n-minnums);
        }
        return res;
    }
};

Solution 2(C++)

class Solution {
public:
    int minMoves(vector<int>& nums) {
        int minnums=INT_MAX;
        int sum=0;
        for(auto n: nums){
            minnums= min(minnums, n);
            sum+= n;
        }
        return sum- minnums* nums.size();
    }
};

算法分析

这道题还是有点意思的。解法一与解法二都是我自己写的,其实本质上都是我自己想的方法解决的。但是,解法二既可以理解为解法一的简化,也可以理解为另一种方法。

解法一: 首先声明,解法一是自己想出来。后面将主要记录自己的思路。

首先自然就是拿了几个数组试一试。取了:[1,2,3]、[1,2,4]、[3,4,5,6]。然后在做的过程中首先发现,移动操作会导致两个结果:一:数组的元素顺序会发生变化;二:其他未被操作的元素数值会加1。但仔细思考就会发现,其实数组的元素顺序变化对结果并没有影响。所以,我们可以简化操作:选中一个元素,该元素数值不变,其他元素数值加一。然后重复观察之前的例子操作过程,发现一般都是最开始选中数组里最大的元素,然后让其他较小的元素数值不断加1,这样其他元素与最大元素的差距就能减小。

然后回顾题目,发现每一次操作都是一个元素数值不变,其他元素数值均加一。如果这个数值不变的元素是最大的元素,那么每次操作,其他较小的元素与该元素的差距会越来越小。最终数组里所有元素都相同,说明元素之间差距为0。

这里反复提到了“差距”的概念,我就联想到了,这个简化后的“移动“操作其实就是不断缩小元素之间的差距。然后,尝试以元素中最大值为标准,其他元素与该标准的差距作为新的元素,构造一个”差距“数组。比如:

[1,2,3] =>[2,1,0];[1,2,4]=>[3,2,0];[3,4,5,6]=>[3,2,1,0]。

那么,每次操作就可以用数学来表示,每次操作就是给这个”差距“数组加上一个一个元素为0,其他都为-1的数组,即:

[2,1,0]+[-1,-1,0] => [1,0,0],对应实际操作为:[1,2,3],移动3,获得:[2,3,3]。可以发现之前的处理完全合理。

但是新的问题来了,如果继续按照这样处理,对于[1,0,0]不能加上一个一个元素为0,其他都为-1的数组。所以,都这里,这个方法遇到了一个困难。

在这时候,我想起了之前做题总结的一个方法:遇到困难的时候,从反方向看,说不定能解决问题。

于是就尝试”反着看“,那么观察这个一个元素为0,其他都为-1的数组,反着来,不就是一个元素为1,其他都为0的数组。那么其实意思就是,每一次操作,一个元素数值不变,其他元素数值加一,这个操作可以理解为其他元素与该元素的差距减少一,这样就得到了一个元素为0,其他都为-1的数组,这是让其他元素数值加一到达差距缩小的目的。也可以反过来,要达到同样的目的,可以让这一个元素数值减小,其他元素不变。、

所以,反过来理解后,就可以找到数组中最小的元素,然后计算其他大的元素与它之间的差距,然后通过大元素减一,不断减少差距的方法,使所有的元素差距都为0,这样就可以有:

[1,2,3]=>[0,1,2]=>[0,0,2]=.[0,0,1]=>[0,0,0]。

由于这样每次操作会让”差距“数组元素总和减少1,那么操作的次数就一定等于”差距“数组所有元素的总和。而差距数组就是通过数组中所有元素与最小元素的差值获得的。

至此,解法一分析完毕。

解法二: 解法二本质是出于简化解法一的代码中的两次循环的原因。因为想写成一个循环,所以对解法一的程序进行分析,发现,第二个循环每次都会减去数组的最小值,如此,便可以通过在第一循环中直接获取所有元素的和,然后减去最小值乘以数组规模的积即可。

但是解法二也可以换一种方式解释。首先观察,数组最开始总和记为sum,然后观察最后的数组一定是所有元素都相同。然后回顾一下,不管如何操作数组,数组最开始的最小元素一定是不断累加的,而每次累加的时候,数组的总和增加为数组规模减一。这样就有公式:

sum + m * (n - 1) = x * n

其中,m是数组操作的次数,x是数组最后相同的元素。考虑到最小元素是一直再增加的,有:

x = minNum + m

那么,联立方程就可以获得:

 sum - minNum * n = m

所以输出m就可以了。

程序分析

这道题主要是算法层面,虽然理解上,解法二的第二种理解简单很多,但是解法一也是对自己思维的一种锻炼,可以尝试理解,我觉得还是很好的,这道题不错。当然了,也要学习简单的解法,我觉得解法一强调问题转化能力,解法二强调问题简化能力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值