vivo 2020届校招在线编程笔试

题目:运矿车

小v最近在玩一款挖矿的游戏,该游戏介绍如下:
1、每次可以挖到多个矿石,每个矿石的重量都不一样,挖矿结束后需要通过一款平衡矿车运送下山;
2、平衡矿车有左右2个车厢,中间只有1个车轮沿着导轨滑到山下,且矿车只有在2个车厢重量完全相等且矿石数量相差不超过1个的情况下才能成功运送矿石,否则在转弯时可能出现侧翻。
假设小v挖到了n(n<100)个矿石,每个矿石重量不超过100,为了确保一次性将n个矿石都运送出去,一旦矿车的车厢重量不一样就需要购买配重砝码。请问小v每次最少需要购买多少重量的砝码呢? (假设车厢足够放下这些矿石和砝码,砝码重量任选)

用例:

输入描述:
输入n个正整数表示每个矿石的重量

输出描述:
输出一个正整数表示最少需要购买的砝码重量

输入例子1:
3 7 4 11 8 10

输出例子1:
1

例子说明1:
小v可以将重量3,7和11的矿石放到左车厢,重量4,8和10 放到右车厢,然后购买重量为1的砝码放到左车厢

想法

一开始毫无头绪,想的都是往两个栈里进出,不得要领。
后来想到假设两个车厢都是满的,不做加减,只做交换,就容易了很多。
小车厢的容量是固定的,就是矿石数量一半下取整。只要小车厢的载重逼近半重,大小车厢的载重差就越小,买的砝码就少。可以将数组分成两个大小车厢,通过循环历遍一节车厢的每一块石头,比较另一个车厢中是否有可以替换的矿石,使该车厢载重最逼近标准线(半重)。
但在如何出这个历遍的时候,出现了麻烦。
后来想到用一个标记量记录连续未替换的次数,如果连续一轮都没有替换的,说明已是最优情况,可以跳出导出结果。

    private static int solution(int[] input) {
        //准备工作---定义小车厢的容量,求小车厢的载重,矿石总重,标准线(半重)
        int len =input.length/2,littleCaseWeight=0,allweight=0;
        final double halfweight;
        for(int i : input){ //求总重
            allweight+=i;
        }
        halfweight=(double)allweight/2;//求半重
        for(int i=0;i<len;i++){
            littleCaseWeight+=input[i];
        }

        int flag=len;
        int index=0;
        while (flag>0){
            flag--;
            double deltaweight=Math.abs(halfweight-littleCaseWeight);
            int swapindex=-1;
            for(int i=len;i<input.length;i++){
                int trylittleCaseWeight=littleCaseWeight-input[index]+input[i];
                if(Math.abs(trylittleCaseWeight-halfweight)<deltaweight){
                    deltaweight=Math.abs(trylittleCaseWeight-halfweight);
                    swapindex=i;
                }
            }
            if(swapindex!=-1){
                littleCaseWeight=littleCaseWeight-input[index]+input[swapindex];//刷新小车厢载重和交换石块位置
                int temp=input[index];
                input[index]=input[swapindex];
                input[swapindex]=temp;
                flag=len;//替换后应该再开一轮

            }
            index=(index+1)%len;//从头循环
        }

        return Math.abs(allweight-2*littleCaseWeight);
    }

后来又想到两个极端情况。
1.重的矿石先放进小车厢。因为容量固定,在这种情况下还无法超过半重,重石块换轻石块更是不可能,就可以直接导出结果了。
2.轻的石块先放小车厢。因为容量固定,在这种情况下还无法低于半重,轻石块换重石块更是不可能,就可以直接导出结果了。
那先对其进行部分选择排序,判断极端情况,应该能提升不少效率。

    private static int solution(int[] input) {
        //准备工作---定义小车厢的容量,求小车厢的载重,矿石总重,标准线(半重)
        int len =input.length/2,littleCaseWeight=0,allweight=0;
        final double halfweight;
        for(int i =0;i<len;i++){//进行部分选择排序,把最重的几块矿石填满小车厢
            int max_index=i;
            for(int j=i;j<input.length-1;j++){
                if(input[j]<input[j+1]){
                    max_index=j+1;
                }
            }
            int temp=input[max_index];
            input[max_index]=input[i];
            input[i]=temp;
        }

        for(int i : input){ //求总重
            allweight+=i;
        }
        halfweight=(double)allweight/2;//求半重
        for(int i=0;i<len;i++){
            littleCaseWeight+=input[i];
        }
        //准备完后可得,小车厢都是重石块,如果小车载重小于标准线,说明换石块是不可能让形成载重逼近标准线,可直接得到结果
        if(littleCaseWeight<=halfweight){
//            for(int i: input)System.out.print(i+",");
            return allweight-2*littleCaseWeight;

        }

        //如果小车载重高于标准线,就需要调换小石块,使其逼近标准线,应该循环每个石块,在大车厢里找一块轻石块替换该石块,让小车厢载重更逼近标准线。如果1正轮都找不到,说明已经最接近了
        int flag=len;
        int index=0;
        while (flag>0){
            flag--;
            double deltaweight=Math.abs(halfweight-littleCaseWeight);
            int swapindex=-1;
            for(int i=len;i<input.length;i++){
                int trylittleCaseWeight=littleCaseWeight-input[index]+input[i];
                if(Math.abs(trylittleCaseWeight-halfweight)<deltaweight){
                    deltaweight=Math.abs(trylittleCaseWeight-halfweight);
                    swapindex=i;
                }
            }
            if(swapindex!=-1){
                littleCaseWeight=littleCaseWeight-input[index]+input[swapindex];//刷新小车厢载重和交换石块位置
                int temp=input[index];
                input[index]=input[swapindex];
                input[swapindex]=temp;
                flag=len;//替换后应该再开一轮

            }
            index=(index+1)%len;//从头循环
        }
        return Math.abs(allweight-2*littleCaseWeight);
    }

题目2

今年7月份vivo迎来了新入职的大学生,现在需要为每个新同事分配一个工号。人力资源部同事小v设计了一个方法为每个人进行排序并分配最终的工号,具体规则是:
将N(N<10000)个人排成一排,从第1个人开始报数;如果报数是M的倍数就出列,报到队尾后则回到队头继续报,直到所有人都出列;
最后按照出列顺序为每个人依次分配工号。请你使用自己擅长的编程语言帮助小v实现此方法。

用例:

输入描述:
输入2个正整数,空格分隔,第一个代表人数N,第二个代表M:

输出描述:
输出一个int数组,每个数据表示原来在队列中的位置用空格隔开,表示出列顺序:

输入例子1:
6 3

输出例子1:
3 6 4 2 5 1

例子说明1:
6个人排成一排,原始位置编号即为1-6。最终输出3 6 4 2 5 1表示的是原来编号为3的第一个出列,编号为1的最后一个出列。

想法

看到题目的第一反应是“约瑟夫环问题”,但细细一品又不一样,所以前几天找的数学解法不能套用,那就只能模拟了。但模拟时发现题目隐藏着两个要求
要求1:重新排队(要求新序列)
要求2:还没有分配工号(保留旧信息)
以N=7,M=3为例。结果是3 6 2 7 5 1 4。
要求新序列是指原来的顺序改变了。保留旧信息,例子可见原第三个的人排第一了但还是没有分配工号1号。

最简单就是安排两个全0数组,一个做原队,另一个做新队,定义自增M为号令。原队循环遍历。每次报道%m= =0的人就把自己的index复制到新队,这样内存开销比较大。

    private static String solution(int[] input) {

        int n=input[0];
        int m=input[1];
        int[] res=new int[n];
        int[] result=new int[n];
        int index=0;
        int signednum=1;
        int M=1;
        int idx=0;
        while(idx<n){
            if(res[index]!=0){
                index=(index+1)%n;
                continue;
            }
            if(M%m==0){
                res[index]=1;
                result[idx]=index+1;
                idx++;
            }
            M++;
            index=(index+1)%n;
        }
        
        StringBuffer ab=new StringBuffer();
			for(int i :result){
    			ab.append(i+" ");
		}
        
        return ab.toString();
    }

另外一种就是单组的推移方法。需要先遍历赋值,每次报道%m==0的人就移到队首,循环的起点也后移。不过这样数组赋值操作很频繁,比较慢。

题目3

小v在公司负责游戏运营,今天收到一款申请新上架的游戏“跳一跳”,为了确保提供给广大玩家朋友们的游戏都是高品质的,按照运营流程小v必须对新游戏进行全方位了解体验和评估。这款游戏的规则如下:
有n个盒子排成了一行,每个盒子上面有一个数字a[i],表示在该盒子上的人最多能向右移动a[i]个盒子(比如当前所在盒子上的数字是3,则表示可以一次向右前进1个盒子,2个盒子或者3个盒子)。
现在小v从左边第一个盒子上开始体验游戏,请问最少需要移动几次能到最后一个盒子上?

用例

输入描述:
输入 :2 2 3 0 4

表示现在有5个盒子,上面的数字分别是2, 2, 3, 0, 4。

输出描述:
输出:2

小v有两种跳法:

跳法1:盒子1 -> 盒子2 -> 盒子3 -> 盒子5,共3下

跳法2:盒子1 -> 盒子3 -> 盒子5,共2下

跳法2的步骤数最少,所以输出最少步数:2。

输入例子1:
2 2 3 0 4

输出例子1:
2

想法

典型的递归求解,只是没说无解时为-1
设数组的解为f()
看这个例子:2 2 3 0 4 和 2 3 0 4, 3 0 4
很容易就得到 f([2 2 3 0 4] )=min( f([2 3 0 4 ] ),f([3 0 4]) )+1
只要将其用编程语言来描述就行了。

另外可以发现,从a往后跳,它的范围是[a的index+1,a的index+a],这样只要在一个循环中分别试探后可达起点数组的次数,取最小值+1返回。如果是空集,就返回默认值(MAX__VALUE。。注意到是比小,如果函数中暂存次数的标记默认结果为-1,当出现一条路不通时,结果就是-1和其他路径次数比较了。所以这里应该设置暂存最小次数的值为极大)

接着分析这个函数需要的参数 :数组 ,次数计数器
数组不用每次都创建新数组,只要添加一个起点参数就可以了。
次数计数器,是每递归一一次,就自增一次的Int;

如果起点就是终点。说明数组已经跳到完结了,返回次数计数器。
,没有到底就看看可达起点的次数,取最小的+1返回。没有可达的点就返回极大值。

这里用的是倒着往前来。

        private static int jump(int endindex ,int[] arr,int deepth ){
        int minpath=Integer.MAX_VALUE;
        if(endindex==0){
            return deepth;
        }
        else {
            for(int i=endindex-1;i>=0;i--){
                if(arr[i]+i>=endindex){
                    int t=jump(i,arr,deepth+1);
                    minpath=t>minpath?minpath:t;
                }
            }

            return minpath;
        }
        
    }
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值