题目描述:
Given a non-empty integer array of size n, find the minimum number of moves required to make all array elements equal, where a move is incrementing n - 1 elements by 1.
Example:
Input: [1,2,3] Output: 3 Explanation: Only three moves are needed (remember each move increments two elements): [1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]
要完成的函数:
int minMoves(vector<int>& nums)
说明:
1、这道题看起来有点意思。
我们先举一个[1,2,3,4]的例子,看能不能发现点什么规律。
[1,2,3,4],挑选里面最小的3个元素+1
[2,3,4,4],挑选里面最小的3个元素+1
[3,4,5,4],挑选里面最小的3个元素+1
[4,5,5,5],挑选里面最小的3个元素+1
[5,6,6,5],挑选里面最小的3个元素+1
[6,7,6,6],挑选里面最小的3个元素+1
[7,7,7,7],挑选里面最小的3个元素+1
我们可以发现在处理vector的时候,总是会选择里面最小的n-1个元素来+1。这样子处理,直觉告诉我们这是最快的。
的确如此,假如在[3,4,5,4]这一步,我们不选择最小的三个元素来处理,而是选择4,5,4来+1,结果会是[3,5,6,5]。后续无论怎样处理都达不到[7,7,7,7]了。
选择最小的n-1个元素来处理,这是最快达到“所有数值都相等”的方法。
按理来说,知道了这些信息,我们就可以写代码来模拟这个过程:
每次找到最大元素,然后把除了最大元素的其他元素+1。
不断重复处理,直到所有元素都相等。
输出重复处理的次数。
但是总觉得这里面有一缕能够直接计算输出的味道。
我们再回过头来看一下。
2、数学方法
n-1个数+1,如果重复处理count次,那么就是(n-1)*count,加上原本n个数的数值,加完结果会等于所有相等的数(设为t)*n。
也就是(n-1)*count+(n个数的数值)=t*n。
关键就在于t的值了。
我们回过头看一下1中举的例子,每一次的处理,最小值1都参与,一共加了6次,最后变成7。
直觉告诉我们,最开始时候最小值会加上count次,也就是t应该是min+count。
如果这个结论成立的话,上述式子就可以解出count来了。
我们尝试一下,代码如下:
int minMoves(vector<int>& nums)
{
int min1=nums[0];//记录最小值
int len=nums.size();
int result=nums[0];//记录nums中的数值的和
for(int i=1;i<len;i++)
{
result+=nums[i];
if(nums[i]<min1)
min1=nums[i];
}
return result-min1*len;//我们要求的count
}
submit,实测51ms,beats 94.55% of cpp submissions。通过了所有测试样例……
我们的直觉为什么是成立的?笔者在推[1,2,3,4]的时候,觉得直觉的想法应该大致是成立的,但是想给出一个数学上的解释就比较麻烦了。
姑且放过笔者吧……日后被remind到简单又好懂的推理方法再来补充。