一道百度笔试题的思考

前几天参加了百度的笔试, 就 AC 了一道题(留下菜鸡的泪水). 第三题没怎么花时间, 但是第二题是花了时间没做出来, 今天在牛客上学习了一下大佬们的代码, 居然还是没太明白(怀疑人生了). 就又去请教了东哥. 

题目

输入两行数 :

  • 第一行 : n (表示数组元素个数, 规模不大, 2 <= n <= 50 如果没记错的话)
  • 第二行 : n 个数 (数值很大, 需要用 long 来表示)

题目要求每次将该数组中最大的数减去 n , 其余的数都加上 1, 这算一次操作, 重复操作直到该数组中最大的数也比 n 小, 请问这个操作的次数是多少. 

 

思路分析

首先考虑的当然是暴力解决, 那么这个问题就化为两个小步骤 : 1. 找到数组中最大的数, 将其减去 n; 2. 将数组中其他的元素加 1. 其实考虑到给定的数据范围就直到直接这样去模拟是会出现超时的. 当然不负众望地走不通. 

先拿出牛客上大佬的代码 : 

作者:还是随意吧~~
链接:https://www.nowcoder.com/discuss/395175
来源:牛客网

import java.util.Scanner;
public class Solution2 {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
 
        int N = s.nextInt();
        long[] nums=new long[N];
        long sum=0;
        for (int i = 0; i < N; i++) {
            nums[i]=s.nextLong();
 
        }
 
        while(!isValid(nums)){
            long max=0;
            int index=0;
            for (int i = 0; i < nums.length; i++) {
                if(max<nums[i]){
                    max=nums[i];
                    index=i;
                }
            }
            sum+=max/N;
            for (int i = 0; i <N ; i++) {
                nums[i]+=max/N;
            }
            nums[index]=max%N;
        }
        System.out.println(sum);
 
    }
    public static boolean isValid(long[] nums){
        boolean res=true;
        for(long l:nums){
            if(l>=nums.length){
                res=false;
            }
        }
        return res;
    }
}

思路就是 : 每次找到最大值 max , 然后赋值 max = max % n, 其他的值都加上 max / n;

我就是卡在这个地方不知道为什么可以这样模拟, 其实这也就是没有再深入一些进行思考. 觉得自己想不出来就去找答案或者帮助. 东哥问我想了多久 ? 我说 10 分钟不到吧. 他说 : 10 分钟不到, 一局 dota 还都是开始, 还能把一个不会的题想明白? 其实就是投入的时间和专注度不够.

其实这里简单想一下 : 假设  max = a * n + b, 那么 max % n == b, max / n = a, 要将 max 转换为 b, 就需要减去 a 个 n; 其他的数在这个过程中正好也是加了 a 个 1, 等于 a. 前面超时的原因就是在模拟的过程中, 减 n, 加 1, 太慢了. 这里就使用模运算和除法运算加快了这个过程.

那为什么可以使用这样来模拟呢 ? 这里取模的前提是把 max 在接下来的 a 次操作里面, 都当成了最大值的条件. 其实这里有一个隐含的等价关系 : 

  1. 每次将最大的减  N, 其他的加 1, 重复直到最大值小于 N
  2. 从左到右, 每次碰到一个比 N 大的数, 就减 N, 其他的加 1, 直到所有的数都小于 N

这两个做法是等价的. 最大值小于 N, 也就是每一个数都小于 N. 结果相同, 先减去 N 再加 1 和先加 1 再减去 N 是等价的. 所以可以将 max - n 变成  max - a * n. 这里 max 减去 n 之后, 可能这个数不是最大值了, 所以满足这个等价关系才能这么模拟. 

实际上还可以省略这个找最大值的过程, 遇到大于 n 的数, 就赋值 x = x % n, 其他的值都加上 x / n;

和东哥聊到, 这个等价关系的发现可能是一种对数学的直觉. 东哥说, 这个感觉是来自于 之前遇到问题的思考, 是一个积累的过程.  应该把不会的问题思考到 觉得很显然很符合直觉, 单纯的记住算法、公式 就很低效, 泛化能力很差.

提高这个能力还可以来画图, 或者手动来模拟这个过程. 我开始是以为最终的状态尽管是所有的数都小于 n, 但是每个数的状态可能不一样, 但是手动模拟之后, 发现每个数的最终状态都是一样的.

其实进一步思考, 就是将所有的大于 n 的数都变成小于 n, 这个中间可能还会有小于 n 的数变大, 变得大于 n. 但是两个过程都是等价的, 最终的加的次数和减的次数应该是一致的.

以后要多思考, 多花时间, 最好能模拟一下, 加深直觉印象.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值