大厂笔试真题解析

1. 华为 喷墨水 (模拟)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
模拟题,塔子哥提交ac

  • 输入处理:双字节十六进制转二进制
    十六进制转十进制再转二进制,读取多个字符串多次处理,给数组arr赋值,长度不足就数组首部即字符末尾用0填充
  • 核心逻辑:分向右,向左移动分别处理
    for循环遍历数组arr,从0开始每次向右/左移动i个位置,检查是否满足条件,满足返回此时移动的步数;
  • check函数逻辑:
    判断不移动和移动了之后,对应arr数组的下标是否为1,为1的话说明此处可以喷墨,步长step+1,最终判断step是否等于数组长度,若是返回true
  • 输出处理
    判断左/右移动标记是否为-1,若不是-1,就返回更新后的数组
package ZhenTi;

import java.sql.SQLOutput;
import java.util.Scanner;

/**
 * 5/6喷墨水
 */
public class Solution29 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        sc.nextLine();
        String[] strs = sc.nextLine().split(" ");
        int[] arr = new int[n];
        for(int i=0,c=0; i<strs.length && c<n; i++){
            //十六进制变十进制变二进制,前两位0x为前缀
            char[] bs = Integer.toBinaryString(Integer.parseUnsignedInt(strs[i].substring(2),16))
                    .toCharArray();
            //长度不足16,后边的用0填充
            for(int j=0;j<16-bs.length&&c<n;j++){
                arr[c++] = 0;
            }
            for(int j=0;j<bs.length&&c<n;j++){
                arr[c++] = bs[j]- '0';
            }
        }
//        System.out.println(arr);
        int ans,ans1=-1,ans2=-1;
        //检查向右移动,从0开始遍历,只求最短的,找到了就返回
        for(int i=0;i<n;i++){
            if(check(arr,i,0)){
                ans1 = i;
                break;
            }
        }
        //检查向左移动
        for(int i=0;i<n;i++){
            if(check(arr,i,1)){
                ans2 = i;
                break;
            }
        }
        ans = (ans1==-1?0:1) + (ans2==-1?0:1);
        System.out.println(ans);
        if(ans1!=-1){
            System.out.println("+" + ans1);
            int[] res = new int[n];
            for(int i=0;i<n;i++){
                if(arr[i]==0){
                    //输出要求堵塞的孔开启即可,因此先找到堵塞的孔处理
                    if(arr[i]==0){
                        res[i-ans1] = 1;
                    }
                }
            }
            for(int item:res){
                System.out.print(item);
            }
            System.out.println();
        }
        if(ans2!=-1){
            System.out.println("-" + ans2);
            int[] res = new int[n];
            for(int i=0;i<n;i++){
                if(arr[i]==0){
                    //输出要求堵塞的孔开启即可,因此先找到堵塞的孔处理
                    if(arr[i]==0){
                        res[i+ans2] = 1;
                    }
                }
            }
            for(int item:res){
                System.out.print(item);
            }
        }


    }
    //cnt表示位移步数,dir表示移动方向,0表示向右移动,1表示向左移动
    public static boolean check(int[] arr,int cnt,int dir){
        int ans = 0;
        int n = arr.length;
        if(dir==0){//向右移动
            for(int i=0,j;i<n;i++){
                //两种情况:不移动可以实现喷墨或者向右移动cnt位也能实现喷墨
                if(arr[i]==1 || ((j=i-cnt)>=0&&arr[j]==1 )){
                    ans++;
                }else{
                    break;
                }
            }
        }else{//向左移动
            for(int i=0,j;i<n;i++){
                //两种情况:不移动可以实现喷墨或者向左移动cnt位也能实现喷墨
                if(arr[i]==1 || ((j=i+cnt)<n&&arr[j]==1 )){
                    ans++;
                }else{
                    break;
                }
            }
        }
        return ans == n;//如果ans等于数组长度n就说明喷墨成功
    }
}

2. 华为 表达式计算 (模拟)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
复杂的模拟题,塔子哥提交AC
分成数字和特殊字符两部分,转换成数字相加,就不需要考虑进位的问题了
首先,遇到特殊字符就将该位换成0,并记录下特殊字符和位置,最后得到数字部分的值相加。 然后计算特殊字符相加的值,也就是题目中表达式的值乘上10的(所在位置相对小数点的位置)幂次方,比如小数点后一位就乘上10的-1幂次方。 最后转字符串处理并输出。

package ZhenTi;
/**
 * 5/6 模拟加法 华为
 */

import java.math.BigDecimal;
import java.util.*;

public class  Solution30{
    static HashMap<String,Double> sp = new HashMap<>();//特殊字符的加法原则
    static StringBuilder spl = new StringBuilder();//记录所有的特殊字符
    static BigDecimal cnt = BigDecimal.valueOf(0.0);//记录处理特殊字符后的数字
    static int count = 0;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        in.nextLine();
        String[] str = in.nextLine().split("\\+");
        String left = str[0];   //第一个数
        String right = str[1];  //第二个数
        HashMap<Integer,Integer> leftSpecial = divide(left);//个数(从0开始):索引
        HashMap<Integer,Integer> rightSpecial = divide(right);//得到cnt 用0替代特殊字符后,两个数字之和
        //加法规则
        sp.put("!+!",0.0);
        sp.put("!+@",13.0);sp.put("@+!",13.0);
        sp.put("!+#",4.0);sp.put("#+!",4.0);
        sp.put("@+@",7.0);
        sp.put("@+#",20.0);sp.put("#+@",20.0);
        sp.put("#+#",5.0);
        //处理特殊字符
        int l = spl.length() / 2;//第一个数字的特殊字符的位置和第二数字对应特殊字符位置相差len/2
        int index = 0;
        for (int i = 0; i < l; i++) {
            String temp = spl.charAt(i) + "+" + spl.charAt(l + i);

            int idx = left.indexOf('.');//判断是否有小数点
            if(idx == -1){//没有小数点, 最终结果为 sum*10^index
                index = left.length() - leftSpecial.get(i) - 1;
            }else{//有小数点,如果特殊字符在整数部分sum*10^index;小数部分,sum*10^-index
                int tem = leftSpecial.get(i);
                if(tem < idx){
                    index = idx - tem - 1;
                }else{
                    index = -(tem - idx);
                }
            }
            Double num = sp.get(temp) * Math.pow(10,index);
            String tempnum = String.valueOf(num);

            BigDecimal bigtemp = new BigDecimal(tempnum);
            cnt = cnt.add(bigtemp);//特殊字符的求和结果
        }

        //处理结果:去除后置0和小数点
        StringBuilder res = new StringBuilder(cnt.toString());
        int length = res.length();
        while(res.charAt(length -1) == '0'){
            res.deleteCharAt(length - 1);
            length--;
        }
        if(res.charAt(length - 1) == '.'){
            res.deleteCharAt(length - 1);
        }
        System.out.println(res);

    }
    //处理数字:把特殊字符变为0
    public static HashMap<Integer,Integer> divide(String str){
        int l = str.length();
        //存在小数点,返回索引;否则返回-1
        int index = str.indexOf(".");
        //保存特殊字符索引
        HashMap<Integer,Integer> Special = new HashMap<>();//count特殊字符个数-1 : i在字母中对应索引
        StringBuilder res = new StringBuilder();
        if(index == -1){         //无小数点
            for (int i = 0; i < l; i++) {  //特殊字符用0替代
                if(str.charAt(i) < '0' || str.charAt(i) > '9'){
                    Special.put(count,i);//放入哈希表
                    count++;//特殊字符统计个数+1
                    spl.append(str.charAt(i));//记录特殊字符
                    res.append('0');//用0代替
                }else{//普通字符直接添加
                    res.append(str.charAt(i));
                }
            }
        }else{           //有小数点,以小数点为间隔做相同的处理 中间加上小数点即可
            for (int i = 0; i < index; i++) {
                if(str.charAt(i) < '0' || str.charAt(i) > '9'){
                    //特殊字符;
                    Special.put(count,i);
                    count++;
                    spl.append(str.charAt(i));
                    res.append('0');
                }else{
                    res.append(str.charAt(i));
                }
            }
            res.append('.');
            for (int i = index + 1; i < l; i++) {
                //有小数点
                if(str.charAt(i) < '0' || str.charAt(i) > '9'){
                    //特殊字符;
                    Special.put(count,i);
                    count++;
                    spl.append(str.charAt(i));
                    res.append('0');
                }else{
                    res.append(str.charAt(i));
                }
            }
        }

        BigDecimal bigres = new BigDecimal(res.toString());
        cnt = cnt.add(bigres);//记录处理特殊字符后的数字,相加,得到数字1和数字2的和
        return Special;
    }
}

3. 华为 魔幻深林救公主 (BFS最短路)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最短路模型
队列元素:x坐标,y坐标,时间轮次
每一个轮次 遍历四个方向 + 停留在原地

package ZhenTi;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Scanner;

/**
 * 5/6华为第三题:找公主
 */
public class Solution31 {
        public static void main(String[] args) {
            Scanner scan = new Scanner(System.in);
            //大小,怪物数量
            int n = scan.nextInt(), k = scan.nextInt();
            boolean[][] traps = new boolean[n][n];//怪物位置
            for (int i = 0; i < k; i++) {
                traps[scan.nextInt()][scan.nextInt()] = true;
            }
            //公主和王子的位置
            int gx = scan.nextInt(), gy = scan.nextInt(), wx = scan.nextInt(), wy = scan.nextInt();
            String[][] strings = new String[n][];
            scan.nextLine();
            for (int i = 0; i < n; i++) {
                strings[i] = scan.nextLine().split(" ");
            }
            char[][][] grid = new char[n][n][3];//地图表格
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    grid[i][j] = strings[i][j].toCharArray();
                }
            }
            boolean[][][] vis = new boolean[n][n][3];
            int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
            Queue<int[]> queue = new ArrayDeque<>();
            queue.offer(new int[]{wx, wy, 0});//两个坐标+时间轮次
            int ans = 0;
            while (!queue.isEmpty()) {
                for (int i = queue.size(); i > 0; i--) {
                    int[] cur = queue.poll();
                    if (cur[0] == gx && cur[1] == gy) {//找到了公主
                        System.out.println(ans);
                        return;
                    }
                    int turn = (cur[2] + 1) % 3;//计算当前轮次
                    for (int[] dir : dirs) {//四个方向搜索
                        int x = cur[0] + dir[0], y = cur[1] + dir[1];
                        //边界,是否有怪物,障碍,是否访问过
                        if (x < 0 || x >= n || y < 0 || y >= n || traps[x][y] || grid[x][y][turn] == '1' || vis[x][y][turn]) continue;
                        queue.offer(new int[]{x, y, turn});
                        vis[x][y][turn] = true;
                    }
                    //可以选择停留在原地,当下一个轮次没有障碍物且未访问过
                    if (grid[cur[0]][cur[1]][turn] != '1' && !vis[cur[0]][cur[1]][turn]) {
                        queue.offer(new int[]{cur[0], cur[1], turn});
                        vis[cur[0]][cur[1]][turn] = true;
                    }
                }
                ans++;//步长
            }
            System.out.println(-1);
        }

}

4. 华为 网格最小可达跳数之和 (BFS最短路)

在这里插入图片描述

package ZhenTi;
/**
 * 网络最小可达跳数之和
 * 多源最短路径BFS
 * 参考leetcodeT417. 太平洋大西洋水流问题
 */

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Solution6 {
    static int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
    static boolean[][] visited;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        String[][] str = new String[n][n];
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                str[i][j] = sc.next();
            }
        }
        visited = new boolean[n][n];
        //开始处理:从目标点反向搜索
        Queue<int[]> que = new LinkedList<>();
        int[][] nums = new int[n][n];//每个点到达终点的步数,BC默认0
        int res = 0;
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if (str[i][j].equals("C")){
                    que.offer(new int[]{i,j});
                }
            }
        }
        while(!que.isEmpty()){
            int[] temp = new int[2];
            temp[0] = que.peek()[0];
            temp[1] = que.poll()[1];
            for(int[] dir:dirs){
                int newX = temp[0] + dir[0];
                int newY = temp[1] + dir[1];
                if(newX<0||newY<0||newX>=n||newY>=n||visited[newX][newY]|| !str[newX][newY].equals("A")){//边界条件+访问过+非A
                    continue;
                }
                que.offer(new int[]{newX,newY});
                visited[newX][newY] = true;
                nums[newX][newY] = nums[temp[0]][temp[1]]+1;
                res += nums[newX][newY];
            }
        }
        System.out.println(res);
    }
}

5. 华为 最佳检测顺序(DFS+自定义排序)

在这里插入图片描述
在这里插入图片描述

构建连接图,dfs找到每个点的前继点的个数
自定义排序,技巧性可以学习
初始化大小不能设置太大,根据题目数据大小设置,一开始多打了个0,一直超时

import java.util.*;


public class Main {
    static Map<Integer, List<Integer>> map = new HashMap<>();//节点:依赖该节点的
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();

        //处理逻辑
        //构建每个点的依赖关系
        //初始化 数据最多100000
        for(int i=0;i<100010;i++){
            map.put(i,new ArrayList<>());
        }
        for(int i=0;i<n;i++){
            int temp = sc.nextInt();
            if(temp!=-1){
                map.get(temp).add(i);//节点:依赖该节点的
            }
        }
        //DFS找到每个节点的依赖点的个数
        List<int[]> res = new ArrayList<>();// [a,b]a表示依赖点的个数,b表示节点序号
        for(int i=0;i<n;i++){
            res.add(new int[]{dfs(i),i});
        }
        //自定义排序
        Collections.sort(res, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if(o1[0]!=o2[0]){
                    return o2[0]-o1[0];
                }else{
                    return Integer.compare(o1[1], o2[1]);
                }
            }
        });
        for(int[] item:res){
            System.out.print(item[1]+" ");
        }
    }
    public static int dfs(int i){
        int count = 0;
        for(int j:map.get(i)){
            count += dfs(j);
        }
        return count+1;
    }
}

6. 美团 塔子哥抓敌人 (二维前缀和)

在这里插入图片描述
在这里插入图片描述
理论学习:
参考文章
在这里插入图片描述

package ZhenTi;

/**
 * https://blog.codefun2000.com/company/mt/3-18/P1087.html#%E4%BB%A3%E7%A0%81
 */
import java.util.Scanner;
public class Solution33 {


    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int a = sc.nextInt();
        int b = sc.nextInt();

        int[][] mp = new int[1011][1011];
        for(int i = 0; i < n; i++) {
            int x = sc.nextInt();
            int y = sc.nextInt();
            mp[x][y]++;
        }

        // 求二维前缀和
        //map[i][j] 表示左上角以(0,0),右下角(i,j)的矩形里有多少个敌人
        for(int i = 1; i <= 1010; i++) {
            for(int j = 1; j <= 1010; j++) {
                //二维前缀和公式推导见资料,右上角被重复算了,需要减
                mp[i][j] += mp[i-1][j] + mp[i][j-1] - mp[i-1][j-1];
            }
        }

        // 枚举技能矩阵的右下角,并求矩形伤害总和的最大值
        int ans = 0;
        for(int i = a+1; i <= 1010; i++) {
            for(int j = b+1; j <= 1010; j++) {
                int t = mp[i][j] - mp[i-a-1][j] - mp[i][j-b-1] + mp[i-a-1][j-b-1];
                ans = Math.max(ans, t);
            }
        }

        System.out.println(ans);
    }

}

7. 用友 滑动窗口最大值 (单调队列)

在这里插入图片描述
leetcode239改版,求两次滑窗,

package ZhenTi;

import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;

public class Solution34 {

    public static void main(String[] args) {
        //为了方便处理下输入
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        String[] sub1 = s.substring(s.indexOf('[')+1,s.indexOf(']')).split(",");
        int[] nums = new int[sub1.length];
        for(int i=0;i<nums.length;i++){
            nums[i] = Integer.parseInt(sub1[i]);
        }
        String[] sub2 = s.substring((s.indexOf('k'))).split("=");
        int k = Integer.parseInt(sub2[sub2.length-1]);
        //开始处理
        int size = nums.length - k + 1;
        int[] resMax = new int[size];
        int[] resMin = new int[size];
        resMax = slidingWindow(nums,k,true);
        resMin = slidingWindow(nums,k,false);
        int res = Integer.MIN_VALUE;
        for(int i=0;i<size;i++){
            res = Math.max(res,resMax[i]-resMin[i]);
        }
        System.out.println(res);
    }
    public static int[] slidingWindow(int[] nums, int k,boolean flag) {
        if(nums.length == 1) return nums;
        int[] res = new int[nums.length-k+1];//所有滑窗里的最大值
        MyDeque que = new MyDeque();
        for(int i=0;i<k;i++){
            que.add(nums[i],flag);
        }
        res[0] = que.peekMax();
        int index = 1;
        for(int i=k;i<nums.length;i++){
            que.removeFirst(nums[i-k]);//删除滑窗第一个元素(可能不存在)
            que.add(nums[i],flag);
            res[index++] = que.peekMax();
        }
        return res;
    }
}
class MyDeque{
    Deque<Integer> deque = new LinkedList<>();
    //增
    //flag为true 单调递减队列
    //flag为false 单调递减增队列
    public void add(int val,boolean flag){
        if(flag){
            while(!deque.isEmpty() && val>deque.getLast()){
                deque.removeLast();
            }
            deque.add(val);
        }else{
            while(!deque.isEmpty() && val<deque.getLast()){
                deque.removeLast();
            }
            deque.add(val);
        }
    }
    //删:删除滑窗的第一个元素,只有相等才需要删除
    public void removeFirst(int val){
        if(!deque.isEmpty()&&val == deque.peek()){
            deque.pollFirst();
        }
    }

    //返回最大值
    public int peekMax(){
        return deque.peek();
    }
}

8. 美团 拆分树(DFS) **

在这里插入图片描述
在这里插入图片描述

import java.util.*;
public class Main{
    static List<Integer>[] edges;
    static int[] counts;
    static int[] eC;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        edges = new List[N+1];
        for (int i = 0; i < edges.length; i++) {
            edges[i] = new ArrayList<>();
        }
        for (int i = 1; i < N; i++) {
            int to = sc.nextInt();
            int from = sc.nextInt();
            edges[to].add(from);
            edges[from].add(to);
        }
        eC = new int[N+1];
        counts = new int[N+1];
        dfs(1,-1);
        dfs1(1,-1,N);
        int ans = Integer.MAX_VALUE;
        int ansCount = 0;
        for (int i = 2; i <= N ; i++) {
            if (ans == eC[i]){
                ansCount++;
            }else if (ans>eC[i]){
                ans = eC[i];
                ansCount = 1;
            }
        }
        System.out.println(ans+" "+ansCount);
    }

    private static void dfs1(int root, int father,int N) {
        List<Integer> edge = edges[root];
        for (Integer integer : edge) {
            if (integer!=father){
                eC[integer] = Math.abs(N-counts[integer]-counts[integer]);
                dfs1(integer,root,N);
            }
        }
    }

    private static void dfs(int root, int father) {
        List<Integer> edge = edges[root];
        int ans = 1;
        for (Integer integer : edge) {
            if (integer!=father){
                dfs(integer,root);
                ans+=counts[integer];
            }
        }
        counts[root] = ans;
    }
}

过了样例 网站没过 可以参考下注释

package ZhenTi;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;

/**
 * https://codefun2000.com/p/P1177
 * 拆分树-dfs 美团
 */
public class Solution35 {
    static List<List<Integer>> edges = new ArrayList<>();//记录相连的边
    static int[] count;//记录每个节点对应的节点个数
    static int[] tempResult;//删除边后(该节点到父节点的边)的两棵树的节点差值
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        //初始化
        int n = sc.nextInt();
        for(int i=0;i<=n;i++){
            edges.add(new ArrayList<>());
        }
        for(int i=1;i<n;i++){
            int child = sc.nextInt();
            int parent = sc.nextInt();
            edges.get(child).add(parent);
            edges.get(parent).add(child);
        }
        count = new int[n+1];
        tempResult = new int[n+1];
        //遍历每个节点对应的节点数:更新count
        //默认父节点为1
        dfs1(1,-1);
        //删除每条边后的答案:更新tempResult,不用dfs也可以
        dfs2(1,-1,n);
        int res = Integer.MAX_VALUE;
        int resCount = 0;
        for(int i=2;i<=n;i++){
            if(res==tempResult[i]){
                resCount++;
            }else if(res>tempResult[i]){
                res = tempResult[i];
                resCount=1;
            }
        }
        System.out.println(res+" "+resCount);
    }
    public static void dfs1(int child,int parent){
        List<Integer> tempEdge = edges.get(child);
        int res = 1;//节点数为1
        for(Integer temp:tempEdge){//遍历每个子节点
            if(temp!=parent){
                dfs1(temp,child);
                res += count[temp];//所有子节点的累加
            }
        }
        count[child] = res;
    }
    public static void dfs2(int child,int parent,int n){
        List<Integer> edge = edges.get(child);
        for(Integer temp:edge){
            if(temp!=parent){
                tempResult[temp] = Math.abs(n-count[temp]-count[temp]);
                dfs2(temp,child,n);
            }
        }
    }
}

9. 阿里 联通块 (并查集) **

在这里插入图片描述
分两种情况,遇到R就变成W,W不用变化;处理逻辑:拿到点,往下搜索满足条件就插入合并为同一组

import java.util.Scanner;

public class Main{
            static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上下左右四个方向
        static int[] father; // 并查集数组
        static int numRedBlocks; // 红色连通块个数

        public static void main(String[] args) {
            //初始化
            Scanner scanner = new Scanner(System.in);
            int n = scanner.nextInt();
            int m = scanner.nextInt();
            scanner.nextLine(); // 读取换行符
            char[][] matrix = new char[n][m];
            for (int i = 0; i < n; i++) {
                matrix[i] = scanner.nextLine().replaceAll(" ", "").toCharArray();
            }
            //
            int[][] res = new int[n][m];
            for(int i=0;i<n;i++){
                for(int j=0;j<m;j++){
                    if (matrix[i][j] == 'R') {
                        matrix[i][j] = 'W';
                        res[i][j] = countRedBlock(matrix,n,m);
                        matrix[i][j] = 'R';
                    }else{
                        res[i][j] = countRedBlock(matrix,n,m);
                    }
                }
            }

            for(int i=0;i<n;i++){
                for(int j=0;j<m;j++){
                    System.out.print(res[i][j]+" ");
                }
                System.out.println();
            }
        }
        public static int countRedBlock(char[][] matrix,int n,int m) {
            father = new int[n * m];//并查集
            numRedBlocks = 0;
            //计算红色区域数量
            for (int i = 0; i < n * m; i++) {
                father[i] = i;
                if (matrix[i / m][i % m] == 'R') {
                    numRedBlocks++;
                }
            }
            //合并红色格子
            for (int i = 0; i < n * m; i++) {
                int row = i / m;
                int col = i % m;
                if (matrix[row][col] == 'R') {
                    for (int[] dir : dirs) {
                        int nRow = row + dir[0];
                        int nCol = col + dir[1];
                        //遇到R:就往下走
                        if (nRow >= 0 && nCol >= 0 && nRow < n && nCol < m && matrix[nRow][nCol] == 'R') {
                            union(i,nRow*m+nCol);//若两个父节点不一致就合并且减去红色区域数量
                        }
                    }
                }
            }
            return numRedBlocks;
        }

        //找父节点
        public static int findRoot(int x){
            if (x != father[x]) {
                father[x] = findRoot(father[x]);
            }
            return father[x];
        }
        //合并:x插入y
        public static void union(int x,int y){
            int rootX = findRoot(x);
            int rootY = findRoot(y);
            if(rootX!=rootY){
                father[rootX] = rootY;
                numRedBlocks--;
            }
        }
}

10. 阿里 数组重排 (贪心)

在这里插入图片描述
在这里插入图片描述
最小对最小,最大对最大;a排序后,b用一个二维数组记录索引,排序后再根据索引求a

package ZhenTi;

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

/**
 * https://codefun2000.com/p/P1100
 */
public class Solution37 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] a = new int[n];
        int[][] b = new int[n][2];
        for(int i=0;i<n;i++){
            a[i] = sc.nextInt();
        }
        for(int i=0;i<n;i++){
            b[i][0] = sc.nextInt();
            b[i][1] = i;
        }
        Arrays.sort(a);
        Arrays.sort(b,(x,y)->{
            return x[0]-y[0];
        });//数组b按数字大小排序
        //把a的值赋值到b
        for(int i=0;i<n;i++){
            b[i][0] = a[i];
        }
        //对更新后的b按索引排序
        Arrays.sort(b,(x,y)->{
            return x[1]-y[1];
        });
        for(int i=0;i<n;i++){
            System.out.print(b[i][0]+" ");
        }
    }
}

11.美团 必经之路(树上DFS) **

在这里插入图片描述
在这里插入图片描述
往两边dfs

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;


public class Main{
    static List<List<Integer>> edges = new ArrayList<>();
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        for(int i=0;i<n;i++){
            edges.add(new ArrayList<>());
        }
        for(int i=2;i<=n;i++){
            int num = sc.nextInt();
            edges.get(num-1).add(i-1);
            edges.get(i-1).add(num-1);
        }
        int x = sc.nextInt()-1;
        int y = sc.nextInt()-1;
        System.out.println(dfs(x,y)+dfs(y,x)+1);//需要加1 本身
    }
    public static int dfs(int start,int end){
        int res = 0;
        for(int num:edges.get(start)){
            if(num!=end){
                res = Math.max(res,dfs(num,start)+1);
            }
        }
        return res;
    }
}

12. 米哈游 RGB矩阵(DFS岛屿数量进阶版)

在这里插入图片描述
在这里插入图片描述
添加链接描述
正常DFS一次,染色后DFS一次,求连通区域

package ZhenTi;

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

/**
 * https://codefun2000.com/p/P1094
 * 岛屿数量进阶版
 */
public class Solution39 {
    static char[][] colors;
    static boolean[][] visited;
    static int[][] dirs = {{-1,0},{1,0},{0,1},{0,-1}};
    static int m;
    static int n;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        m = sc.nextInt();
        n = sc.nextInt();
        sc.nextLine();
        colors = new char[m][n];
        visited = new boolean[m][n];
        for(int i=0;i<m;i++){
            colors[i] = sc.nextLine().toCharArray();
        }
        //整除情况
        int count1=0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(!visited[i][j]){
                    dfs(i,j);
                    count1++;
                }
            }
        }
        int count2 = 0;
        //染色
        for(int i=0;i<m;i++){
            Arrays.fill(visited[i],false);
        }
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(colors[i][j]=='G'){
                    colors[i][j] = 'B';
                }
            }
        }
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(!visited[i][j]){
                    dfs(i,j);
                    count2++;
                }
            }
        }
        System.out.println(count1-count2);
    }
    public static void dfs(int x,int y){
        visited[x][y] = true;

        for(int[] dir:dirs){
            int nx = x + dir[0];
            int ny = y + dir[1];
            if(nx>=0&&ny>=0&&nx<m&&ny<n&&colors[x][y]==colors[nx][ny]&&!visited[nx][ny]){
                dfs(nx,ny);
            }
        }
    }
}

13. 华为od 开心消消乐 (BFS)

在这里插入图片描述
在这里插入图片描述

package ZhenTi;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

/**
 * https://codefun2000.com/p/P1275
 * 华为 开心消消乐
 */
public class Solution40 {
    static int m;
    static int n;
    static int[][] nums;
    static int[][] dirs = {{-1,0},{1,0},{0,1},{0,-1},{1,-1},{1,1},{-1,1},{-1,-1}};
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        m = sc.nextInt();
        n = sc.nextInt();
        nums = new int[m][n];
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                nums[i][j] = sc.nextInt();
            }
        }
        int res = 0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(nums[i][j]==1){
                    bfs(i,j);//遇到1就开始染色
                    res++;
                }
            }
        }
        System.out.println(res);
    }
    public static void bfs(int x,int y){
        Queue<int[]> que = new LinkedList<>();
        que.offer(new int[]{x,y});
        while(!que.isEmpty()){
            int[] temp = que.poll();
            for(int[] dir:dirs){
                int nx = temp[0] + dir[0];
                int ny = temp[1] + dir[1];
                if(nx>=0&&nx<m&&ny>=0&&ny<n&&nums[nx][ny]==1){
                    nums[nx][ny] = 0;//更新染色点
                    que.offer(new int[]{nx,ny});
                }
            }
        }
    }
}

14. 华为 跑步(BFS) **

在这里插入图片描述
在这里插入图片描述

BFS Queue(X,Y,速度) + 状态记录
输出不好处理

package ZhenTi;
/**
 * https://codefun2000.com/p/P1295
 * 华为 BFS 加一个维度
 */

import java.util.*;

public class Solution41 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String[] s = sc.nextLine().trim().split(",");
        int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]);
        s = sc.nextLine().trim().split(",");
        int sx = Integer.parseInt(s[0]), sy = Integer.parseInt(s[1]);
        int[][] h = new int[n][m], o = new int[n][m];
        s = sc.nextLine().trim().replace(" ", ",").split(",");
        for (int i = 0, idx = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                h[i][j] = Integer.parseInt(s[idx++]);
            }
        }
        s = sc.nextLine().trim().replace(" ", ",").split(",");
        for (int i = 0, idx = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                o[i][j] = Integer.parseInt(s[idx++]);
            }
        }//输入

        // 状态记录
        Map<Integer, Set<Integer>> map = new HashMap<>();

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                map.put(i * m + j, new HashSet<>());//对于每一个点开辟一个set记录出现过的速度
            }
        }
        Queue<int[]> q = new LinkedList<>();//开辟队列
        map.get(sx * m + sy).add(1);
        q.offer(new int[]{sx, sy, 1});//初始点入队列
        int cnt = 0;
        int[][] dirs = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
        while (!q.isEmpty()) {
            int[] t = q.poll();
            int x = t[0], y = t[1], v = t[2];
            for (int[] dir : dirs) {
                int a = x + dir[0], b = y + dir[1];
                if (a < 0 || a >= n || b < 0 || b >= m) continue;//上面几句BFS经典操作
                int nv = v + h[x][y] - h[a][b] - o[a][b];//求得速度
                if (nv <= 0 || map.get(a * m + b).contains(nv)) continue;//一旦发现(x,y,v)已经被记录或者v<=0时就跳过

                if (nv == 1) cnt++;//如果(x,y,1)第一次发生,答案就加1
                map.get(a * m + b).add(nv);//更新map,存储每一个(x,y,v)
                q.offer(new int[]{a, b, nv});//BFS经典操作
            }
        }
        System.out.println(cnt);
    }
}

15. 美团 RGB树(树形dp)

在这里插入图片描述
在这里插入图片描述

import java.util.*;

public class Main{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int size = sc.nextInt();
        List<Integer>[] edges = new List[size+1];
        for(int i=1;i<=size;i++){
            edges[i] = new ArrayList<>();
        }
        for(int i=2;i<=size;i++){
            int n = sc.nextInt();
            edges[i].add(n);
            edges[n].add(i);
        }
        char[] colors = new char[size+1];
        sc.nextLine();
        String str = sc.nextLine();
        for(int i=0;i<size;i++){
            colors[i+1] = str.charAt(i);
        }
        //处理
        //dp[i][3] 第i个节点为根节点,对应的三种颜色的数量
        //dfs1记录每个点
        //dfs2找到切割方案的次数
        int[][] dp = new int[size+1][3];
        boolean[] visited = new boolean[size+1];
        dfs1(edges,colors,visited,dp,1);
        Arrays.fill(visited,false);
        System.out.println(dfs2(edges,colors,visited,dp,1));
    }
    public static void dfs1(List<Integer>[] edges,char[] colors,boolean[] visited,int[][] dp,int index){
        //更新访问状态,遍历三种颜色
        visited[index] = true;
        if(colors[index]=='R'){
            dp[index][0]++;
        }
        if(colors[index]=='G'){
            dp[index][1]++;
        }
        if(colors[index]=='B'){
            dp[index][2]++;
        }
        //遍历所连接的每一条边
        for(int temp:edges[index]){
            if(visited[temp]){continue;}
            dfs1(edges,colors,visited,dp,temp);//更新子节点
            for(int i=0;i<3;i++){
                dp[index][i] += dp[temp][i];//父节点等于子节点之和
            }
        }
    }
    public static int dfs2(List<Integer>[] edges,char[] colors,boolean[] visited,int[][] dp,int index){
        int res = 0;
        visited[index] = true;
        if(index!=1&&check(dp[index],dp[1])){
            res++;
        }
        for(int next:edges[index]){
            if(visited[next]) {continue;}
            res += dfs2(edges,colors,visited,dp,next);
        }
        return res;
    }
    //判断左子树或者右子树是否满足条件
    public static boolean check(int[] cur,int[] root){
        for(int i=0;i<3;i++){
            if(cur[i]==0||root[i]-cur[i]==0){
                return false;
            }
        }
        return true;
    }
}

16. 阿里巴巴 满二叉树的数量(记忆化搜索)

在这里插入图片描述
在这里插入图片描述

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

/**
 * https://codefun2000.com/p/P1082
 * 阿里 树形dp (记忆化搜索)
 */
class TreeNode{
    int val;
    TreeNode left;
    TreeNode right;
    public TreeNode(int val){
        this.val = val;
    }
}
public class Main {
    static int[] dp;//-2表示未搜索过 -1表示不是满二叉树 n表示节点个数
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        dp = new int[n+1];
        Arrays.fill(dp,-2);//要先初始化为-2
        TreeNode[] nodes = new TreeNode[n+1];
        for(int i=1;i<n+1;i++){
            nodes[i] = new TreeNode(i);
        }
        for(int i=1;i<=n;i++){
            int left = sc.nextInt();
            int right = sc.nextInt();
            if(left!=-1) nodes[i].left = nodes[left];
            if(right!=-1) nodes[i].right = nodes[right];
        }
        int res = 0;
        for(int i=1;i<=n;i++){
            if(dfs(nodes[i])>=0){res++;}
        }
        System.out.println(res);
    }
    //以节点node开始搜索,三种情况:1.已经搜索过;2.
    public static int dfs(TreeNode node){
        if(node == null) return 0;
        if(dp[node.val]!=-2) return dp[node.val];//记忆化搜索
        int left = dfs(node.left);
        int right = dfs(node.right);
        if(left==-1||right==-1||left!=right){
            dp[node.val] = -1;//不是满二叉树
        }else{
            dp[node.val] = left+1;
        }
        return dp[node.val];
    }
}

17. 美团 南北对决 (前缀和)

在这里插入图片描述

import java.util.*;
public class Main{
        public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        while(n>0){
            int m = sc.nextInt();
            int[] nums = new int[m];
            int[] res = new int[m];
            for(int i=0;i<m;i++){
                nums[i] = sc.nextInt();
            }
            //前到后遍历
            int[][] prev = new int[m][2];
            if(nums[0]==1){
                prev[0][1] = 1;
            }else{
                prev[0][0] = 0;
            }
            for(int i=1;i<m;i++){
                if(nums[i]==0){
                    prev[i][0] = prev[i-1][0] + 1;
                    prev[i][1] = prev[i-1][1];
                }else{
                    prev[i][1] = prev[i-1][1] + 1;
                    prev[i][0] = prev[i-1][0];
                }
            }
            int[] right = new int[2];
            right[0] = prev[m-1][0];
            right[1] = prev[m-1][1];
            //后到前遍历
            int[][] next = new int[m][2];
            if(nums[m-1]==1){
                next[m-1][1] = 1;
            }else{
                next[m-1][0] = 0;
            }
            for(int i=m-2;i>=0;i--){
                if(nums[i]==0){
                    next[i][0] = next[i+1][0] + 1;
                    next[i][1] = next[i+1][1];
                }else{
                    next[i][1] = next[i+1][1] + 1;
                    next[i][0] = next[i+1][0];
                }
            }
            int[] left = new int[2];
            left[0] = next[0][0];
            left[1] = next[0][1];
            //
            for(int i=0;i<m;i++){
                if(nums[i]==0){
                    res[i] = right[0]-prev[i][0]+left[1]-next[i][1];
                }else{
                    res[i] = right[1]-prev[i][1]+left[0]-next[i][0];
                }
                System.out.print(res[i]+" ");
            }
            System.out.println("");
            n--;
        }
    }
}

18. 美团 染色的树 (DFS)

在这里插入图片描述
在这里插入图片描述

import java.util.*;
public class Main{
    static List<List<Integer>> child = new ArrayList<>();
    static List<Integer> colors = new ArrayList<>();
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        for(int i=0;i<n;i++){
            child.add(new ArrayList<>());
        }
        for(int i= 1;i<n;i++){
            child.get(sc.nextInt()-1).add(i);
        }
        for(int i=0;i<n;i++){
            colors.add(sc.nextInt());
        }
        System.out.println(dfs(0));
    }
    public static int dfs(int n){
        if(child.get(n).isEmpty()){//空节点返回1
            return 1;
        }
        int sum = 0;
        //1个节点和2个节点 A^0=A A+0=0
        for(int next:child.get(n)){
            if(colors.get(n)==1){
                sum += dfs(next);//红色累加
            }else{
                sum ^= dfs(next);//绿色异或
            }
        }
        return sum;
    }
}

19. 百度 排列数量 (技巧)

在这里插入图片描述

import java.util.*;

public class Main{
        public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        while(n-->0){
            int m = sc.nextInt();
            int[][] nums = new int[m][2]; //[数字,索引]
            for(int j=0;j<m;j++){
                nums[j][0] = sc.nextInt();
                nums[j][1] = j;
            }
            //按照数字大小排序
            Arrays.sort(nums,(a,b)->(a[0]-b[0]));
            int max = Integer.MIN_VALUE;
            int min = Integer.MAX_VALUE;
            int res = 0;
            //索引范围包含数字即是排列
            for(int k=0;k<m;k++){
                max = Math.max(max,nums[k][1]);
                min = Math.min(min,nums[k][1]);
                if(max-min<=k){
                    res++;
                }
            }
            System.out.println(res);
        }
    }
}

20. 腾讯音乐 价值二叉树 (树形DFS)

在这里插入图片描述
在这里插入图片描述

import java.util.*;
public class Main{
    static int size;
    static List<List<Integer>> joinList;
    static int[] value;
    static int[] res;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        size = sc.nextInt();
        joinList = new ArrayList<>();
        value = new int[size];
        res = new int[size];
        for(int i=0;i<size;i++){
            value[i] = sc.nextInt();
        }
        for(int i=0;i<size;i++){
            joinList.add(new ArrayList<>());
        }
        for(int i=0;i<size-1;i++){
            int from = sc.nextInt()-1;
            int to = sc.nextInt()-1;
            joinList.get(from).add(to);
        }
        dfs(0);
        for(int i=0;i<size;i++){
            System.out.print(res[i]+" ");
        }
    }

    public static int[] dfs(int index){
        int[] split = splitTwoFive(value[index]);    //计算每个数分解成2和5的个数
        int[] tempRes = new int[2];
        for(int next:joinList.get(index)){
            tempRes = dfs(next);//取子节点的值,更新
            split[0] += tempRes[0];
            split[1] += tempRes[1];
        }
        res[index] = Math.min(split[0],split[1]);//结果由最小值决定
        return split;
    }

    public static int[] splitTwoFive(int node){
        int count2 = 0;
        int count5 = 0;
        while(node != 0){
            if(node%2==0){
                count2++;
                node = node/2;
            }else{
                break;
            }
        }
        while(node != 0){
            if(node%5==0){
                count5++;
                node = node/5;
            }else{
                break;
            }
        }
        return new int[]{count2,count5};
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值