Skip to main content

美团2024届秋招笔试第一场编程真题

·928 words·5 mins
WFUing
Author
WFUing
A graduate who loves coding.
Table of Contents
  • 题目链接:https://www.nowcoder.com/exam/company?currentTab=recommand&jobId=100&selectStatus=0&tagIds=179

这次的笔试,很多签到题,没什么难度,主要是第 3 题和第 9 题,比较有难度。

3.小美的树上染色
#

在做的时候,很幸运的从后往前遍历就过了,应该是用例水了。

import java.util.Scanner;
import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);
        while (in.hasNextInt()) {
            int n = in.nextInt();
            int[] a = new int[n];
            int u, v;
            Set<Integer> temp =  new HashSet<>();
            for (int i = 0; i < n; i++) {
                a[i] = in.nextInt();
            }
            int ans = 0;
            int [][]conn = new int[n - 1][2];
            for (int i = 0; i < n - 1; i++) { 
                conn[i][0] = in.nextInt();
                conn[i][1] = in.nextInt();
                conn[i][0] -= 1;
                conn[i][1] -= 1;
            }
            for (int i = n-2; i >= 0; i--) {
                if ( !temp.contains(conn[i][0]) && !temp.contains(conn[i][1]) 
                && Math.pow((int)Math.sqrt(a[conn[i][0]] * a[conn[i][1]]),
                              2) == (a[conn[i][0]] * a[conn[i][1]]) ) {
                    temp.add(conn[i][0]);
                    temp.add(conn[i][1]);
                    ans+=2;
                }
            }
            System.out.println(ans);
        }
    }

}

1. 树形DP解法
#

树形DP的好题。

对于每个节点,引入0,1状态(表示不染色,染色)

那每个子树的根节点u, s为u的儿子节点集合

import java.io.BufferedInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class Main {

    static class Solution {
        int[] ws;
        List<Integer> []g;
        int[][] dp;

        int solve(int n, int[] ws, List<Integer>[] g) {
            this.g = g;
            this.dp = new int[n + 1][2];
            this.ws = ws;
            dfs(1, -1);
            return Math.max(dp[1][0], dp[1][1]);
        }

        void dfs(int u, int fa) {
            int[] res = new int[] {0, 0};
            for (int v: g[u]) {
                if (v == fa) {
                    continue;
                }
                dfs(v, u);
                res[0] += Math.max(dp[v][0], dp[v][1]);
            }

            for (int v: g[u]) {
                if (v == fa) continue;
                int x2 = res[0] - Math.max(dp[v][0], dp[v][1]);

                long rr = (long)ws[u] *ws[v];
                long r = (long)Math.sqrt(rr);
                if (r * r == rr) {
                    res[1] = Math.max(2 + x2 + dp[v][0], res[1]);
                }
            }
            dp[u][0] = res[0];
            dp[u][1] = res[1];
        }

    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        int n = sc.nextInt();
        int[] ws = new int[n + 1];
        for (int i = 1; i <= n ;i++) {
            ws[i] = sc.nextInt();
        }
        List<Integer>[]g = new List[n + 1];
        Arrays.setAll(g, x -> new ArrayList<>());

        for (int i = 0; i < n - 1; i++) {
            int u = sc.nextInt();
            int v = sc.nextInt();
            g[u].add(v);
            g[v].add(u);
        }

        Solution solution = new Solution();
        int res = solution.solve(n, ws, g);
        System.out.println(res);
    }

}

2. 无权二分图最大匹配
#

看到评论区有大佬,提到了这个方法,所以补充一下。

匈牙利算法,其时间复杂度 $O(V*E)$, 树的节点$N$,边$N-1$,理论会达到$O(10^{10})$。

感觉还是测试数据偏随机,完全平方数限制很强,导致时间复杂度骤降。

import java.io.BufferedInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class Main {

    // 无权二分图最大匹配算法 O(VE)
    static boolean match(int u, int[] link, boolean[] used, List<Integer> []g) {
        for (int v: g[u]) {
            if (used[v]) continue;
            used[v] = true;
            if (link[v] == 0 || match(link[v], link, used, g)) {
                link[u] = v;
                link[v] = u;
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        int n = sc.nextInt();
        int[] ws = new int[n + 1];
        for (int i = 1; i <= n ;i++) {
            ws[i] = sc.nextInt();
        }
        List<Integer>[]g = new List[n + 1];
        Arrays.setAll(g, x -> new ArrayList<>());

        for (int i = 0; i < n - 1; i++) {
            int u = sc.nextInt();
            int v = sc.nextInt();
            long rr = (long)ws[u] * ws[v];
            long r = (long)Math.sqrt(rr);
            if (r * r == rr) {
                g[u].add(v);
                g[v].add(u);
            }
        }

        int ans = 0;
        int[] link = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            if (link[i] != 0) continue;
            boolean[] used = new boolean[n + 1];
            used[i] = true;
            if (match(i, link, used, g)) {
                // 找到一条增广路径
                ans++;
            }
        }

        System.out.println(ans * 2);
    }

}

9.小美的字符串变换
#

此题是经典的并查集模板题。首先生成行列乘积为n的二阶矩阵,再根据矩阵中元素上下左右值是否与之相等选择是否union,最后得到并查集中集合数量。取所有二阶矩阵并查集中集合数量最小值即为解。

import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        char[] str = sc.next().toCharArray();
        int q = n;
        for (int i = 1; i <= n; i++) {
            if (n % i == 0) {
                int N = i;
                int M = n / i;
                int[][] board = new int[N][M];
                int index = 0;
                for (int j = 0; j < N; j++) {
                    for (int k = 0; k < M; k++) {
                        board[j][k] = str[index++];
                    }
                }
                q = Math.min(q, q(board));
            }
        }
        System.out.println(q);
    }

    public static int q(int[][] board) {
        int N = board.length;
        int M = board[0].length;
        UnionFind uf = new UnionFind(N * M);
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                int cur = i * M + j;
                if (i - 1 >= 0 && board[i - 1][j] == board[i][j]) {
                    uf.union(cur - M, cur);
                }
                if (i + 1 < N && board[i + 1][j] == board[i][j]) {
                    uf.union(cur + M, cur);
                }
                if (j - 1 >= 0 && board[i][j - 1] == board[i][j]) {
                    uf.union(cur - 1, cur);
                }
                if (j + 1 < M && board[i][j + 1] == board[i][j]) {
                    uf.union(cur + 1, cur);
                }
            }
        }
        return uf.size();
    }

    public static class UnionFind {
        private int[] parent;
        private int[] sizeMap;
        private int size;

        public UnionFind(int N) {
            size = N;
            parent = new int[N];
            sizeMap = new int[N];
            for (int i = 0; i < N; i++) {
                parent[i] = i;
                sizeMap[i] = 1;
            }
        }

        public int size() {
            return size;
        }

        private int find(int v) {
            if (v != parent[v]) {
                parent[v] = find(parent[v]);
            }
            return parent[v];
        }

        public void union(int v1, int v2) {
            int f1 = find(v1);
            int f2 = find(v2);
            if (f1 != f2) {
                size--;
                int s1 = sizeMap[f1];
                int s2 = sizeMap[f2];
                if (s1 > s2) {
                    parent[f2] = f1;
                    sizeMap[f1] += s2;
                } else {
                    parent[f1] = f2;
                    sizeMap[f2] += s1;
                }
            }
        }
    }
}