记录一些笔试题及题解(相关大厂笔试题)

1.移动元素

描述

小明获得了一个仅包含正整数且每个元素各不相同的数组,显然数组里存在最大值与最小值。小明每次能够交换两个相邻的数字,他想知道,将最大值与最小值分别移动到数组两端,最少需要交换多少次?

输入输出

输入: 第一行输入一个正整数n,数组元素的个数。
接下来一行为n个各不相同的正整数,第i个数代表数组内第i个元素(1≤i≤n)。
1≤n≤10^5
1≤a_i≤1e9
**输出:**一个正整数,代表最少需要交换的次数。
示例:
输入:
5
4 5 3 1 2
输出:
2

思路

首先,我们需要找到数组中的最大值和最小值,以及它们的位置。
然后,我们需要将最大值和最小值移动到数组的两端。这里有两种可能的情况:一种是将最大值移动到数组的左端,最小值移动到数组的右端;另一种是将最小值移动到数组的左端,最大值移动到数组的右端。我们需要计算出这两种情况下的交换次数,然后取最小的那个。

对于第一种情况,我们需要将最大值移动到数组的左端,最小值移动到数组的右端。这需要的交换次数为pos1 +(n-1)-pos2。其中,pos1是最大值的位置,pos2是最小值的位置,n是数组的长度。
对于第二种情况,我们需要将最小值移动到数组的左端,最大值移动到数组的右端。这需要的交换次数为pos2 +(n-1)-pos1

代码

package stu.lbc.algorithm.code.writtentest;

import java.util.Scanner;
//
//小明获得了一个仅包含正整数且每个元素各不相同的数组,显然数组里存在最大值与最小值。
// 小明每次能够交换两个相邻的数字,他想知道,将最大值与最小值分别移动到数组两端,最少需要交换多少次?
//第一行输入一个正整数n,数组元素的个数。
//接下来一行为n个各不相同的正整数,第i个数代表数组内第i个元素(1≤i≤n)。
//输出一个正整数,代表最少需要交换的次数。
public class Baidu01_MovingElements {
    public static void main(String[] args) {
        Scanner in=new Scanner(System.in);
        int n= in.nextInt();
        int[] nums=new int[n];
        for (int i = 0; i < n; i++) {
            nums[i]=in.nextInt();
        }
        int max=0,min=Integer.MAX_VALUE,maxIndex=0,minIndex=0;
        for (int i = 0; i < n; i++) {
            if (max<nums[i]){
                max=nums[i];
                maxIndex=i;
            }
            if (min>nums[i]){
                min=nums[i];
                minIndex=i;
            }
        }
        int res_1=maxIndex+(n-1)-minIndex;
        int res_2=minIndex+(n-1)-maxIndex;
        System.out.println(Math.min(res_1,res_2));
    }
}

2.有向图的节点

描述

给定一个有向图,一共有n个节点以及n条边,问:从1号节点出发,k步之内能够到达哪些节点?

输入输出

输入:
第一行两个整数n,k,表示节点的数量,以及最多走的步数
第二行n个整数a_i,表示从i到a_i​​ 有一条有向边
输出:
能到达的节点的编号,按从小到大的顺序输出。
示例:
输入:
5 100
5 4 5 2 3
输出:
1 3 5

思路

题目要求从1号节点出发,k步之内能够到达哪些节点。这是一个典型的图遍历问题,我们可以使用广度优先搜索(BFS)来解决。
首先,我们需要创建一个队列,将1号节点放入队列,并将其标记为已访问。
然后,我们开始进行BFS。在每一轮中,我们首先获取当前队列的大小,这个大小就是当前步数可以到达的节点数量。然后,我们遍历这些节点,对于每一个节点,我们将其所有未访问过的邻居节点放入队列,并将这些邻居节点标记为已访问。在这个过程中,我们需要记录当前的步数,如果步数达到k,我们就停止遍历。

代码

//给定一个有向图,一共有n个节点以及n条边,问:从1号节点出发,k步之内能够到达哪些节点?
//第一行两个整数n,k,表示节点的数量,以及最多走的步数
//第二行n个整数a_i,表示从i到a_i 有一条有向边
//能到达的节点的编号,按从小到大的顺序输出。
public class Baidu02_DirectedGraphNodes {
    public static void main(String[] args) {
        Scanner in=new Scanner(System.in);
        int n=in.nextInt();
        long k=in.nextLong();
        //保存有向图
        LinkedList<Integer>[] graph=new LinkedList[n+1];
        //记录已访问的节点
        boolean[] vis=new boolean[n+1];
        //记录访问的节点,从小到达
        ArrayList<Integer> res=new ArrayList<>();

        //初始化有向图
        Arrays.setAll(graph,e->new LinkedList<>());

        for (int i = 1; i <= n; i++) {
            //得到节点
            int node=in.nextInt();
            //记录点i可以访问的节点
            graph[i].add(node);
        }
        //广度搜索
        ArrayDeque<Integer> queue=new ArrayDeque<>();
        //第一个节点先入堆
        vis[1]=true;
        queue.offer(1);
        res.add(1);
        long depth=0;
        while (!queue.isEmpty()){
            int sz=queue.size();
            depth++;
            for (int i=0;i<sz;++i){
                int t=queue.poll();
                for(int x:graph[t]){
                    if (!vis[x]&&depth<k){
                        vis[x]=true;
                        queue.offer(x);
                        res.add(x);
                    }
                }
            }
        }
        Collections.sort(res);
        System.out.println(res);
    }
}

3.小李移瓶子

描述

小李初始位于 ( a , b ) (a,b) a,b位置,二维平面上有个瓶子,每个瓶子的位置为 ( x i , y i ) (x_i,y_i) (xi,yi),小李每次可以 向上、下、左、右移动一格,每次移动的代价为1,小李需要每次移动到一个瓶子的位置上,然后拿起瓶子把它放到 ( c , d ) (c,d) (c,d)位置,每次最多只能拿一个瓶子。请问最少需要多少代价才能把所以瓶子都放到 ( c , d ) (c,d) (c,d)位置上。

输入输出

输入:
第一行四个整数a,b,c,d(-109<=a,b,c,d<=109)表示小塔初始位置和瓶子需要放置的位置。
接下来一行一个整数n(1<=n<=105)表示瓶子的数量。
接下来n行,每行两个整数 x i , y i x_i,y_i xi,yi(-109<= x i , y i x_i,y_i xi,yi<=109)表示第i个瓶子的位置。
输出:
输出一个整数,表示最少需要多少代价。
示例:
输入:
0 0 1 1
2
1 0
2 2
输出:
6

思路

除了第一次从 ( a , b ) (a,b) a,b出发外,后面均是从 ( c , d ) (c,d) (c,d)出发再回到 ( c , d ) (c,d) (c,d),所以需要确定我们去的第一个点是哪个。枚举该点并计算其它点到的路径总和即可。

代码

//小塔初始位于$(a,b)$位置,二维平面上有个瓶子,每个瓶子的位置为$(x_i,y_i)$,小塔每次可以 向上、下、左、右移动一格,每次移动的代价为1,
// 小塔需要每次移动到一个瓶子的位置上,然后拿起瓶子把它放到$(c,d)$位置,每次最多只能拿一个瓶子。
// 请问最少需要多少代价才能把所以瓶子都放到$(c,d)$位置上。
//第一行四个整数a,b,c,d(-10^9^<=a,b,c,d<=10^9^)表示小塔初始位置和瓶子需要放置的位置。
//接下来一行一个整数n(1<=n<=10^5^)表示瓶子的数量。
//接下来n行,每行两个整数$x_i,y_i$(-10^9^<=$x_i,y_i$<=10^9^)表示第i个瓶子的位置。
public class Meituan01_MovesTheBottle {
    public static void main(String[] args) {
        Scanner in=new Scanner(System.in);
        int a=in.nextInt();
        int b=in.nextInt();
        int c=in.nextInt();
        int d=in.nextInt();
        int n=in.nextInt();
        int min=Integer.MAX_VALUE;
        int ans=0;
        for (int i = 0; i < n; i++) {
            //点的坐标
            int x=in.nextInt();
            int y=in.nextInt();
            //得到从瓶子所在到目标点的距离
            int t=Math.abs(x-c)+Math.abs(y-d);
            //记录从目标点到瓶子所在处再返回
            ans+=2*t;
            //计算从(a,b)到当前点的距离减去从(c,d)到当前点的距离目的是为了找到离起始点最近的瓶子
            //从而确定我们一开始去哪个点
            int dist=Math.abs(x-a)+Math.abs(y-b)-t;
            if (dist<min){
                min=dist;
            }
        }
        System.out.println(ans+min);
    }
}

4.小李算数

描述

小李有三个数字 a , b , c a,b,c a,b,c他每次操作可以选择一个数字将其加一,最多可以操作 k k k次,小李想知道 a ∗ b ∗ c a*b*c abc的最大值是多少?由于这个数字可能很大,因此你需要输出答案对 1 0 9 + 7 10^9+7 109+7取模后的结果。

输入输出

输入:
第一行四个整数a,b,c,k(1<=a,b,c,d<=1018)表表示数字和操作次数。
输出:
输出一个整数表示答案
示例:
输入:
1 2 3 1
输出:
12

思路

很简单的思想,想让乘积最大则每次都让三个数中的最小值+1

代码

import java.util.Arrays;
import java.util.Scanner;

//小李有三个数字$a,b,c$他每次操作可以选择一个数字将其加一,最多可以操作$k$次,
// 小李想知道$a*b*c$的最大值是多少?由于这个数字可能很大,因此你需要输出答案对$10^9+7$取模后的结果。
//第一行四个整数a,b,c,k(1<=a,b,c,d<=10^18^)表表示数字和操作次数。
//输出一个整数表示答案
public class Meituan01_XiaoLiArithmetic {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        //使用long接收
        long[] nums=new long[3];
        nums[0] = sc.nextLong();
        nums[1] = sc.nextLong();
        nums[2] = sc.nextLong();
        long k= sc.nextLong();
        long mod=1000000007;
        //对其排序
        Arrays.sort(nums);
        //得到最大值和其他数的差值,即三个数都等于最大值需要加1的次数
        long needed=(nums[2]-nums[0])+(nums[2]-nums[1]);
        //如果加1的次数大于差值则剩余三者均分
        if (k>=needed){
            //k减完差值后还剩的次数
            k-=needed;
            //三者先均分
            nums[0]+=k/3;
            nums[1]+=k/3;
            nums[2]+=k/3;
            //均分后剩下的值
            //判断是剩下多少后再分
            long reminder=k%3;
            if (reminder==1){
                nums[0]+=1;
            }else{
                nums[0]+=1;
                nums[1]+=1;
            }
        //如果k小于needed
        }else {
            //如果k不够让最小值等于第二小的值则先给最小的加
            if (k<=nums[1]-nums[0]){
                nums[0]+=k;
            }else {
                //如果够则先将最小值大小加到第二小同等大小后均分
                nums[0]=nums[1];
                k-=(nums[1]-nums[0]);
                nums[0]+=k/2+k%2;
                nums[1]+=k/2;
            }
        }
        long res=((nums[0])%mod)*((nums[1])%mod)*((nums[2])%mod);
        System.out.println(res);
    }
}
  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值