算法筆試題整理——升級蓄水池 && 字符串數字表達式計算值 && 求旅游完所有景點需要的最少天數 && 寶箱怪


1. 小米筆試題——升級蓄水池

題目描述:

在米兔生活的二維世界中,建造蓄水池非常簡單。

一個蓄水池可以用n個坐標軸上的非負整數表示,代表區間為【0-n】范圍內寬度為1的牆壁的高度。

如下圖1,黑色部分是牆壁,牆壁的高度是[0,1,0,2,1,0,1,3,2,1,2,1] ,藍色部分是蓄水的面積,可以看出蓄水池最大蓄水容量是6。

現在米兔想通過增加某些牆壁的高度對蓄水池擴容,但是經費有限,最多只能增加最多m的高度,增加高度只能在【0-n】范圍內,高度為0的區域也是可以增加的,為了追求最大的性價比,米兔想要找到一種最優方案,使擴容后蓄水池的容量最大,你能幫幫他么?

提示:

對於樣例,圖2,圖3,是樣例可能的兩種擴容方案,顯然圖2是比圖3更優的方案

關於題目數據范圍 

20%的數據, n<10,m<10,蓄水池最大高度小於10

50%的數據, n<100,m<100,蓄水池最大高度小於100

100%的數據, n<1000,m<1000,蓄水池最大高度小於1000

輸入

第一行為一個數字n

接下來n行,每行一個數字,代表n個牆壁的高度

最后一行為一個數字m

輸出

一個數字,表示擴容之后蓄水池能達到的最大容量

樣例輸入

12

0

1

0

2

1

0

1

3

2

1

2

1

2

樣例輸出

12

分析

LeetCode上 trap water 題目的改進版, 當時沒有解出這題,現在回想起來可以用dfs來遍歷所有的可能。 

import java.util.*; public class LeetCode { static int maxCapicity = 0; 
  public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[] array = new int[n]; for (int i = 0; i < n; i++) { array[i] = sc.nextInt(); } int m = sc.nextInt(); maxCapicity=capacity(array); dfs(0,array,m); System.out.println(maxCapicity); } static void dfs(int index, int[] array, int m) { if(index>=array.length) return; if(m==0){ maxCapicity=Math.max(maxCapicity, capacity(array)); } for (int i = 0; i <= m; i++) { array[index] += i; dfs(index + 1, array, m - i); array[index] -= i; } } static int capacity(int[] array) { int ans = 0; int n = array.length; int[] left_max = new int[n]; int[] right_max = new int[n]; left_max[0] = array[0]; right_max[n - 1] = array[n - 1]; for (int i = 1; i < array.length; i++) { left_max[i] = Math.max(left_max[i - 1], array[i]); } for (int i = array.length - 2; i >= 0; i--) { right_max[i] = Math.max(right_max[i + 1], array[i]); } for (int i = 0; i < n; i++) { ans += Math.min(right_max[i], left_max[i]) - array[i]; } return ans; } }

能解出示例的輸入,但是沒有平台提供的測試用例所以不知道是否能通過所有的測試用例,僅供參考。

 

2. 嗶哩嗶哩筆試題——字符串數字表達式計算值 

輸入幾行字符串形式的表達式,只包含非負整數,加號,減號和乘號,輸入其計算值。輸入以字符串END結束

輸入樣例:

3+4*5*6+4-7*4+4-2

3+4*2-1

END

輸出

101

10

分析

將原字符串按照加減號分割成字符串數組,計算每個的實際值后再壓入棧中。之后倒序掃描原數組,遇到加號或者減號就從棧中彈出兩個數字,計算值后再壓入棧,遇到乘號則無視。最后棧中剩下的數字就是計算結果。

實際測試法發現分割后的字符串計算后的值應該倒序進棧,然后順序掃描原數組才對,否則在計算減號時會有錯誤。

import java.util.*; public class LeetCode { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String str = sc.nextLine(); List<String> expressions = new ArrayList<>(); List<Integer> ans = new ArrayList<>(); while (!str.equals("END")) { expressions.add(str); str = sc.nextLine(); } for (String s : expressions) ans.add(calculate(s)); for (int a : ans) { System.out.println(a); } } private static int calculate(String str) { Stack<Integer> digit = new Stack<>(); String[] strs = str.split("\\+|\\-"); for (int i = strs.length - 1; i >= 0; i--) { String s = strs[i]; if (s.contains("*")) { String[] multi = s.split("\\*"); int result = 1; for (String mu : multi) result *= Integer.parseInt(mu); digit.push(result); } else { digit.push(Integer.parseInt(s)); } } for (int i = 0; i < str.length(); i++) { if (str.charAt(i) == '+' || str.charAt(i) == '-') { int num1 = digit.pop(); int num2 = digit.pop(); int result = (str.charAt(i) == '+' ? num1 + num2 : num1 - num2); digit.push(result); } } return digit.peek(); } }

 

3. 求旅游完所有景點需要的最少天數

去一個地方出差,順便取旅游,根據導游提供的方案,每一天可以去當地的一個景點。選擇從某一天開始自己的假期,使得在最短的天數內能去所有導游提供的景點。

輸入示例1:(開始一個數字表示總共有多少天。然后第一列表示第幾天,第二列表示第幾天可以去的景點編號)

7
1,7
2,5
3,1
4,7
5,2
6,5
7,1

輸出:

4

即從第2天開始假期,持續到第5天,總共4天。或者從第5天開始假期,也是4天。

輸入示例2:

11
1,2
2,3
3,1
4,3
5,3
6,7
7,2
8,2
9,1
10,1
11,3

輸出:

 5,即從第3天到第7天。

分析

可以通過循環遍歷的方式來窮舉所有的可能,不過會超時,這題關鍵時要求用有效率的方法來解。想到的法還是根據循環遍歷而來,只不過原來的循環會有重復計算的可能,我們要去掉這個重復計算的部分。當從以位置 i 為起始點向后遍歷的時候,記住不同景點的出現次數。如果到了某一點 j 使得從 i 到 j 能夠取到所有景點時,停止向后。轉而從i的下一位開始,更新當前能到達的景點的計數器,如果此時還能到達所以景點則繼續往下一位,如果不能則往 j 之后繼續遍歷直至能到達所有景點,重復這個過程即可。(我感覺有點像Leetcode中Minimum Window Substring這一題的解法,額,看了一下,感覺根本是一個題目 )

import java.util.*; public class LeetCode { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); String[] trip = new String[n]; sc.nextLine(); for (int i = 0; i < n; i++) { String[] strs = sc.nextLine().split(","); trip[i] = strs[1];  // 數組索引直接0~n-1對應第1天到第n天
 } // 先遍歷一遍確定有幾個不同的景點
    Set<String> spots = new HashSet<>(); for (String s : trip) { spots.add(s); } Map<String, Integer> map = new HashMap<>();  // 景點計數器,若要能到達所有景點則至少每個景點要到達一次
    for (String s : spots) { map.put(s, 0); } int begin = 0, end = 0, d = Integer.MAX_VALUE; while (end < n) { // 先從begin=0開始,找左邊第一個包含所有景點的時間段
      String str = trip[end]; if (map.containsKey(str)) map.put(str, map.get(str) + 1); //檢查當前是否能夠到達所有景點
      boolean canTravelAll = true; for (String s : spots) { if (map.get(s) <= 0) canTravelAll = false; } //如果可以則回到左邊,往左移縮小范圍,如果不能則繼續在右邊向后移動
      while (canTravelAll) { d = Math.min(d, end - begin); // 更新最小范圍
        String s = trip[begin]; map.put(s, map.get(s) - 1); // 左移后要判斷當前是否包括了所有的景點,是的話繼續左移
        if (map.get(s) <= 0) canTravelAll = false; } end++; } System.out.println(d); } }

 

4. 寶箱怪

寶箱怪是游戲中常見的一種怪物,它們偽裝成普通的寶箱,並在被玩家打開時攻擊玩家。假設你操控的游戲角色身處一個放着N個寶箱的房間,每個寶箱或者是普通的寶箱,或者是寶箱怪。每個寶箱上都貼着一張字條,字條上寫着以下兩種信息中的一種:

①第x個寶箱是普通寶箱;

②第x個寶箱是寶箱怪。

其中普通寶箱上的信息一定是真的,而寶箱怪上的信息可能是假的,那么根據這些信息,有多少個寶箱一定是普通寶箱,又有多少個寶箱一定是寶箱怪?

輸入

第一行包含一個整數N,1≤N≤105

接下來N行,第i行包含兩個整數t和x,1≤t≤2,1≤x≤N。若t=1,則第i個寶箱上的信息為:第x個寶箱是普通寶箱;若t=2,則第i個寶箱上的信息為:第x個寶箱是寶箱怪

輸出       

輸出兩個以空格隔開的整數,第一個整數表示可以確定為普通寶箱的寶箱數量,第二個整數表示可以確定為寶箱怪的寶箱數量。

樣例輸入

3

1 2

2 1

1 3

樣例輸出

0 1

分析

感覺這題在測智商,就目前遇到的算法題來說,一部分時有套路的,利用dfs或者bfs,dp解之即可。有的其實是數學問題,有的感覺更像是測智商的問題。在牛客網的討論上看到一個比較靠譜的解答:https://www.nowcoder.com/discuss/118930?type=0&order=0&pos=7&page=1

首先要明確的一點是無法確定普通寶箱的數量的,第一個輸出數字一定是0,因為寶箱怪可能說真話也可能說假話,就算知道一個寶箱說的是真話也無法確定它到底時寶箱還是寶箱怪。

所以能確定的只可能是寶箱怪,那么怎么確定呢?如果從它給的結論出發,我們一直往下推,結果得到了矛盾的結論,那么可以證明它的話是假話,那么這個寶箱便是寶箱怪。舉個例子:

寶箱1說寶箱2是普通寶箱,寶箱2說寶箱3是普通寶箱,寶箱3說寶箱1是寶箱怪。那么如果寶箱1是普通寶箱,說的是真話,那么可以推出寶箱1是寶箱怪,與假設不符。那么假設寶箱1是寶箱怪,在這種情形下,寶箱2和3說的都是真話,整個邏輯鏈下來沒問題。

即從結果往前推的話,真話推不出寶箱還是寶箱怪,而假話可以推出必定是寶箱怪。

所以基本的算法的思路是,先按照上面的思路確定一遍寶箱怪,然后如果有其它寶箱說這些寶箱怪是寶箱的也必定是寶箱怪。

代碼

public class LeetCode { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int N = sc.nextInt(); sc.nextLine(); int[] types = new int[N + 1]; int[] nodes = new int[N + 1]; Set<Integer> monster = new HashSet<>(); for (int i = 1; i <= N; i++) { String[] inputs = sc.nextLine().split(" "); types[i] = Integer.parseInt(inputs[0]); nodes[i] = Integer.parseInt(inputs[1]); } int last = 0; // 遍歷判斷每個寶箱是不是寶箱怪
    for (int i = 1; i <= N; ++i) { int count = 0; int k = i; while (types[k] == 1) { k = nodes[k]; if (++count >= N) break;  // 處理環
 } if (types[k] == 2 && nodes[k] == i) monster.add(i); } // 指向寶箱怪 是 寶箱的 都是 寶箱怪
    while (last != monster.size()) { last = monster.size(); for (int i = 1; i <= N; i++) { if (types[i] == 1 && monster.contains(nodes[i])) monster.add(i); } } System.out.println(0 + " " + monster.size()); } }


免責聲明!

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



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