zoukankan      html  css  js  c++  java
  • 2020微软笔试题

    1.将N个数字划分成N/K组,每组K个数字,每组中的数字互不相同,求每个子数组最大最小元素的差值之和的最小值。

    输入:

    N=12, K=4

    arr=3,3,4,4,5,5,6,6,8,8,10,10

    输出:

    15

    {3,4,5,6} 6-3=3

    {3,4,8,10} 10-3=7

    {5,6,8,10} 10-5=5

    3+7+5=15

    思路:先对数组从小到大排序,统计重复数字出现最多的个数,如果大于N/K的话,不能划分,直接返回0。

    然后枚举每一种情况,dfs+剪枝。

    int ans = Integer.MAX_VALUE;
    
    public int solve(int N, int K, int[] arr) {
        Arrays.sort(arr);
        Map<Integer, Integer> map = new HashMap<>();
        int dup_max = 0;
        for (int i = 0; i < arr.length; i++) {
            map.put(arr[i], map.getOrDefault(arr[i], 0) + 1);
            dup_max = Math.max(dup_max, map.get(arr[i]));
        }
        //重复数字出现最多的个数,如果大于N/K的话,不能划分,直接返回0
        if (dup_max > N / K) {
            return 0;
        }
        int[] num = new int[N];
        int[] flag = new int[N];
        //用于判断分组中的重复元素
        Set<Integer> set = new HashSet<>();
        helper(N, K, 0, arr, num, flag, set);
        return ans;
    
    }
    
    //dfs + 剪枝
    public void helper(int N, int K, int index, int[] arr, int[] num, int[] flag, Set<Integer> set) {
        //index==N,说明分组完毕,计算每个子数组最大最小元素的差值之和
        if (index == N) {
            int res = 0;
    
            for (int i = 0; i < num.length; i += K) {
                int min = Integer.MAX_VALUE;
                int max = Integer.MIN_VALUE;
                for (int j = i; j < i + K; j++) {
                    min = Math.min(min, num[j]);
                    max = Math.max(max, num[j]);
                }
                res += Math.abs(max - min);
            }
    
            if (res < ans) {
                ans = res;
                for (int i = 0; i < num.length; i++) {
                    System.out.print(num[i] + " ");
                }
                System.out.println();
                System.out.println(res);
            }
    
            return;
        }
    
    
        for (int i = 0; i < N; i++) {
            //剪枝
            if (flag[i] == 0) {
                if (set.contains(arr[i])) {
                    continue;
                }
                set.add(arr[i]);
                flag[i] = 1;
                num[index] = arr[i];
                if ((index + 1) % K == 0) {
                    set = new HashSet<>();
                }
                helper(N, K, index + 1, arr, num, flag, set);
                set.remove(arr[i]);
                flag[i] = 0;
                num[index] = 0;
    
            }
        }
    }
    

    2.给一个字符串,每次可以移除其中一个字符,或者移除一个回文子串,求 全部移除所需最少次数

    例如:1,4,3,1,5. 先移除 3,再移除 1 4 1,再移除 5,得到最少次数 3.

    leetcode原题:https://leetcode.com/problems/palindrome-removal/description/

    题解:https://coordinate.wang/index.php/archives/2737/

    两种情况:

    1. 直接删除
    2. 找到该字符串对应的回文串,再删除

    img

    image-20200326214226330

    image-20200326221201626

    i+1==k考虑回文子串长度为空的情况

    //动态规划。在原来题解的基础上,加了注释。
    public static int minimumMoves(int[] arr) {
        int n = arr.length;
        //dp[i][j]表示删除从i到j的数字所需的最少操作次数
        int[][] dp = new int[n + 1][n + 1];
        //l表示当前数字的长度
        for (int l = 1; l <= n; l++) {
            int i = 0, j = l - 1;
            while (j < n) {
                if (l == 1) {
                    //base,每个数字的删除次数为1
                    dp[i][j] = 1;
                } else {
                    //不考虑回文子串的情况下,删除次数为之前的删除次数+1
                    dp[i][j] = 1 + dp[i + 1][j];
                    //考虑回文子串
                    for (int k = i + 1; k <= j; k++) {
                        if (arr[i] == arr[k]) {
                            //更新dp[i][j]
                            dp[i][j] = Math.min(dp[i][j], dp[i + 1][k - 1] + dp[k + 1][j] + (i + 1 == k ? 1 : 0));
                        }
                    }
                }
                i++;
                j++;
            }
        }
        return dp[0][n - 1];
    
    }
    

    3.给一个无向图,N个顶点,M条边,0为起点,N-1为终点,每条边初始权值为 1。图中除普通节点外有 4 种节点。

    第一种:走过这种节点后的两条边权值翻倍(Sand)

    第二种:走过这种节点后的两条边权值减半 (Nitro)

    第三种:走到这个节点就停止,不能再走了(Cop)

    第四种:走到这个节点,下一条边的权值+1(Crash)

    求节点 0 到 N-1 的最短权值和路径。

    类似于图的深度优先遍历,需要进行回溯

    //Path记录起点到终点的路径path和花费cost
    class Path {
     List<Integer> path;
     double cost;
    
     public Path(List<Integer> path, double cost) {
         this.path = path;
         this.cost = cost;
     }
    }
    
    //存储最短路径
    Path minPath = new Path(new ArrayList<>(), Integer.MAX_VALUE);
    
    public int[] minTimes(int city, String[] strs, int road, int[][] arr) {
     Map<Integer, Set<Integer>> map = new HashMap<>();
     for (int i = 0; i < arr.length; i++) {
         Set<Integer> set;
         if (!map.containsKey(arr[i][0])) {
             set = new HashSet<>();
         } else {
             set = map.get(arr[i][0]);
         }
         set.add(arr[i][1]);
         map.put(arr[i][0], set);
     }
     Path path = new Path(new ArrayList<>(), 0);
    
     dfs(0, city - 1, strs, map, path);
    
     int[] res = new int[minPath.path.size()];
     for (int i = 0; i < res.length; i++) {
         res[i] = minPath.path.get(i);
     }
     return res;
    
    }
    //计算起点到终点的花费
    public double cal(String[] strs, Path path) {
     double res = 0;
     int size = path.path.size();
     double[] cost = new double[size - 1];
     Arrays.fill(cost, 1);
     for (int i = 0; i < size; i++) {
         if (strs[path.path.get(i)].equals("Nitro")) {
             if (i < size - 1) {
                 cost[i] *= 0.5;
             }
             if (i + 1 < size - 1) {
                 cost[i + 1] *= 0.5;
             }
    
         } else if (strs[path.path.get(i)].equals("Sand")) {
             if (i < size - 1) {
                 cost[i] *= 2;
             }
             if (i + 1 < size - 1) {
                 cost[i + 1] *= 2;
             }
    
         } else if (strs[path.path.get(i)].equals("Crash")) {
             if (i < size - 1) {
                 cost[i] += 1;
             }
         }
    
     }
    
     for (int i = 0; i < cost.length; i++) {
         res += cost[i];
     }
     return res;
    
    }
    //图的深度优先遍历
    public void dfs(int begin, int end, String[] strs, Map<Integer, Set<Integer>> map, Path path) {
     if (begin == end) {
         path.path.add(end);
         double cost = cal(strs, path);
         if (cost < minPath.cost) {
             // 一定要使用new ArrayList<>(path.path)。
             // 直接传入path.path,会导致返回的minPath.path为空,这是由于Java的值传递导致的。
             minPath = new Path(new ArrayList<>(path.path), cost);
             //                System.out.println("cost:" + cost);
             //                System.out.println("minPath" + minPath.path.toString());
         }
         //回溯
         path.path.remove(Integer.valueOf(end));
    
         return;
     }
    
     Set<Integer> set = map.get(begin);
     //寻找下一个可访问的节点
     for (int nextCity : set) {
         if (path.path.contains(nextCity) || strs[nextCity].equals("Cop")) {
             continue;
         }
         path.path.add(begin);
         dfs(nextCity, end, strs, map, path);
         //回溯
         path.path.remove(Integer.valueOf(begin));
    
     }
    
    }
    

    测试用例:

    Test1:
     int city = 5;
     int road = 5;
     String[] strs = {"None", "Cop", "None", "None", "None"};
     int[][] map = {{0, 1}, {0, 2}, {1, 2}, {2, 3}, {3, 4}};
    
     result:{0,2,3,4}
    
    Test2:
     int city1 = 7;
     int road1 = 8;
     String[] strs1 = {"None", "Cop", "Sand", "None", "Nitro", "None", "None"};
     int[][] map1 = {{0, 1}, {0, 2}, {1, 2}, {2, 3}, {2, 4}, {3, 6}, {4, 5}, {5, 6}};
    
     result:{0,2,4,5,6}
    
  • 相关阅读:
    发现个atan2的正确使用方式
    Forward+ Shading架构
    fatal: unable to connect to gitee.com: gitee.com[0: 180.97.125.228]: errno=Unknown error 解决方案
    HDFS HA(高可用性)集群规划
    如何使用RTP引擎对语音编码进行转码
    关于 Angular 应用 tsconfig.json 中的 target 属性
    浅谈 Orbeon form builder 的权限控制
    关于 Angular 应用 tsconfig.json 中的 lib 属性
    orbeon form 通过 url 的方式同第三方应用集成的开发明细
    orbeon form 的配置介绍
  • 原文地址:https://www.cnblogs.com/dockerchen/p/12578881.html
Copyright © 2011-2022 走看看