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}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM