美團2024屆暑期實習第一輪後端筆試詳解

qi66發表於2023-03-12


這是美團2024屆暑期實習後端崗位的第一輪筆試,總共有五道程式設計題,四道 情景演算法題,一道 二叉樹題目,時長兩個小時,我用的是go語言,只AC了前兩道,第三道死活通不過,第四道模擬情況太複雜,放棄了,第五道馬上寫完,可惜沒時間了,還是得合理分配時間才行,哭死!!!

Coding 一

題目描述:

小美有一個由數字字元組成的字串。現在她想對這個字串進行一些修改。 具體地,她可以將文個字串中任意位置字元修改為任意的數字字元。她想知道,至少進行多少次修改,可以使得“修改後的字串不包含兩個連續相同的字元?

例如,對於字串”111222333", 她可以進行3次修改將其變為” 121212313"。
輸入描述

一行,一個字串s,保證s只包含數字字元。1<=|s|<= 100000
輸出描述

一行,一個整數,表示修改的最少次數。

思路:

本題可以使用回溯,也可以使用動態規劃解決,下面是動規的兩種解決方法

func main() {
   var s string
   fmt.Scan(&s)
   length := len(s)
   dp := make([][10]int, length+1)
   for i := 1; i <= length; i++ {
      for j := 0; j < 10; j++ {
         dp[i][j] = length
      }
      for j := 0; j < 10; j++ {
         if int(s[i-1]) == '0'+j {
            for k := 0; k < 10; k++ {
               if j != k {
                  dp[i][j] = min(dp[i][j], dp[i-1][k])
               }
            }
         } else {
            for k := 0; k < 10; k++ {
               if j != k {
                  dp[i][j] = min(dp[i][j], dp[i-1][k]+1)
               }
            }
         }
      }
   }
   res := length
   for i := 0; i < 10; i++ {
      res = min(res, dp[length][i])
   }
   fmt.Println(res)
}

func main1() {
   scanner := bufio.NewScanner(os.Stdin)
   scanner.Scan()
   s := scanner.Text()
   n := len(s)

   dp := make([][2]int, n) // dp[i][0]表示s[i]不變的最小修改次數,dp[i][1]表示s[i]改為另一個數字的最小修改次數

   for i := 0; i < n; i++ {
      if i == 0 {
         dp[i][0] = 0
         dp[i][1] = 1
      } else {
         if s[i] == s[i-1] {
            dp[i][0] = dp[i-1][1]     // s[i]不變,必須將s[i-1]改為另一個數字
            dp[i][1] = dp[i-1][0] + 1 // s[i]改為另一個數字,s[i-1]可以不變或改為另一個數字
         } else {
            dp[i][0] = dp[i-1][0]     // s[i]不變,s[i-1]不變或改為另一個數字都可以
            dp[i][1] = dp[i-1][1] + 1 // s[i]改為另一個數字,s[i-1]不變或改為另一個數字都可以
         }
      }
   }

   fmt.Println(min(dp[n-1][0], dp[n-1][1]))
}

func min(a, b int) int {
   if a > b {
      return b
   }
   return a
}

Coding 二

題目描述:

小團在一個n*m的網格地圖上探索。 網格地圖上第i行第j列的格子用座標(i,j)簡記。初始時,小團的位置在地圖的左上角,即座標(1,1)。 地圖上的每個格子 上都有一定的金幣, 特別地,小團位於的初始位置(1,1)上的金幣為0。小團在進行探索移動時,可以選擇向右移動-格(即從(x,y)到達(x,y+1))或向下移動一格(即從(x,y)到達(x+1,y)) 。地圖上的每個格子都有一個顏色,紅,色或藍色。如果小團次移動前後的兩個格子顏色不同,那麼他需要支付k個金幣才能夠完成這-次移動;如果移動前後的兩個格子顏色相同,則不需要支付金幣。小團可以在任意格子選擇結束探索。現在給你網格地圖上每個格子的顏色與金幣數量,假設小團初始時的金幣數量為0,請你幫助小團計算出最優規劃,使他能獲得最多的金幣,輸出能獲得的最多 金幣數量即可。
注意:要求保證小團任意時刻金幣數量不小於零。

輸入描述

第一行是三個用空格隔開的整數n、m和k,表示網格地圖的行數為n,列數為m,在不同顏色的兩個格子間移動需要支付k個金幣。

接下來n行,每行是一個長度為m的字串, 字串僅包含字元R'或' B'。第i行字串的第j個字元表示地圖上第i行第j列的格子顏色,如果字元為' R' 則表示格子顏色為紅色,為’B' 表示格子顏色為藍色。

接下來是個n行m列的非負整數矩陣,第i行第j列的數字表示地圖上第行第j列的格子上的金幣數量。保證所有資料中數字大小都是介於[0, 10]的整數。

1<=n,m<=200, 1<=k<=5。

輸出描述

一行 一個整數, 表示小團能獲得的最多 金幣數量。

func main() {
   scanner := bufio.NewScanner(os.Stdin)
   scanner.Scan()
   line := strings.Split(scanner.Text(), " ")
   n, _ := strconv.Atoi(line[0])
   m, _ := strconv.Atoi(line[1])
   k, _ := strconv.Atoi(line[2])
   grid := make([][]rune, n)
   for i := 0; i < n; i++ {
      scanner.Scan()
      grid[i] = []rune(scanner.Text())
   }
   coins := make([][]int, n)
   for i := 0; i < n; i++ {
      coins[i] = make([]int, m)
      scanner.Scan()
      for j, v := range strings.Split(scanner.Text(), " ") {
         coins[i][j], _ = strconv.Atoi(v)
      }
   }
   // 初始化dp
   dp := make([][]int, n)
   for i := 0; i < n; i++ {
      dp[i] = make([]int, m)
      for j := 0; j < m; j++ {
         dp[i][j] = -1
      }
   }
   dp[0][0] = 0
   for i := 0; i < n; i++ {
      for j := 0; j < m; j++ {
         if dp[i][j] == -1 {
            continue
         }
         //右
         if j+1 < m {
            c := 0
            if grid[i][j] != grid[i][j+1] {
               c = k
            }
            dp[i][j+1] = max(dp[i][j+1], dp[i][j]+coins[i][j+1]-c)
         }
         //下
         if i+1 < n {
            c := 0
            if grid[i][j] != grid[i+1][j] {
               c = k
            }
            dp[i+1][j] = max(dp[i+1][j], dp[i][j]+coins[i+1][j]-c)
         }
      }
   }
   fmt.Println(dp[n-1][m-1])
}
func max(a, b int) int {
   if a > b {
      return a
   }
   return b
}

Coding 三

題目描述:

小美是位天文愛好者, 她收集了接下來段時間中所有 會劃過她所在的觀測地上空的流星資訊。具體地,她收集了n個流星在她所在觀測地上空的出現時刻和消失時刻。對於一個流星,若'其的出現時刻為s,消失時刻為t,那麼小美在時間段[s, t]都能夠觀測到它。對於一個時刻,觀測地上空出現的流星數量越多,則小美認為該時刻越好。小美希望能夠選擇一個最佳的時刻進行觀測和攝影,使她能觀測到最多數量的流星。現在小美想知道 ,在這個最佳時刻,她最多能觀測到多少個流星以及一共有多少個最佳時刻可供她選擇。

輸入描述

第一行是一個正整數n,表示流星的數量。

第二行是n個用空格隔開的正整數,第i個數si表示第i個流星的出現時間。

第三行是n個用空格隔開的正整數,第i個數ti表示第i個流星的消失時間。

1<=n<=100000, 1<=si<=ti<=10^9

輸出描述

輸出一行用空格隔開的兩個數x和y,其中x表示小美能觀測到的最多流星數,y表示可供她選擇的最佳時刻數量。

演算法思路:

首先,我們將每個流星的出現和消失時間轉換為一系列時間事件,每個事件包括時間點和流星數量變化。然後,按時間點對這些事件進行排序。接下來,我們從左到右遍歷這些事件,並統計當前觀測地上空的流星數量。在遍歷過程中,我們記錄最大的流星數量以及達到最大數量的時間點個數。最終,輸出最大數量和時間點個數即可。

時間複雜度:$O(n\log n)$,其中 $n$ 是流星的數量。我們需要對所有流星的出現和消失時間進行排序,時間複雜度為 $O(n\log n)$。接下來,我們遍歷這些事件,時間複雜度為 $O(n)$。因此,總時間複雜度為 $O(n\log n)$。
空間複雜度:$O(n)$,我們需要儲存每個流星的出現和消失時間,以及每個時間點的流星數量變化。

func main() {
   scanner := bufio.NewScanner(os.Stdin)

   // 讀取流星數量
   scanner.Scan()
   n := toInt(scanner.Bytes())

   // 讀取每個流星的出現和消失時間
   starts := make([]int, n)
   ends := make([]int, n)
   for i := 0; i < n; i++ {
      scanner.Scan()
      starts[i] = toInt(scanner.Bytes())
   }
   for i := 0; i < n; i++ {
      scanner.Scan()
      ends[i] = toInt(scanner.Bytes())
   }

   // 將每個時間點的流星數量統計出來
   events := make([][2]int, 2*n)
   for i := 0; i < n; i++ {
      events[2*i][0] = starts[i]
      events[2*i][1] = 1
      events[2*i+1][0] = ends[i] + 1
      events[2*i+1][1] = -1
   }
   sort.Slice(events, func(i, j int) bool {
      return events[i][0] < events[j][0]
   })

   // 找出最佳時刻
   maxCount := 0
   bestTimes := 0
   count := 0
   for i := 0; i < len(events); i++ {
      count += events[i][1]
      if count > maxCount {
         maxCount = count
         bestTimes = 1
      } else if count == maxCount {
         bestTimes++
      }
   }

   fmt.Printf("%d %d\n", maxCount, bestTimes)
}

func toInt(b []byte) int {
   n := 0
   for _, c := range b {
      n = n*10 + int(c-'0')
   }
   return n
}

Coding 四

題目描述:

小D和小W最近在玩坦克大戰,雙方操控自己的坦克在16*1 6的方格圖上戰鬥,小D的坦克初始位置在地圖的左上角,朝向為右,其座標(0,0), 小W的坦克初始位置在地圖右下角,朝向為左,座標為(15,15)。坦克不能移動到地圖外,坦克會佔領自己所在的格子,己方的坦克不可以進入對方佔領過的格子。每一個回合雙方必須對自己的坦克下達以下5種指令中的一種:

.移動指令U:回合結束後,使己方坦克朝向為上,若上方的格子未被對方佔領,則向當前朝向移動一個單位(橫座標-1),否則保持不動;

.移動指令D:回合結束後,使己方坦克朝向為下,若下方的格子未被對方佔領,則向當前朝向移動一個單位(橫座標+1),否則保持不動,

.移動指令L:回合結束後,使己方坦克朝向為左,若左側的格子未被對方佔領,則向當前朝向移動一個單位(縱座標-1) ,否則保持不動;

.移動指令R:回合結束後,使己方坦克朝向為右,若右側的格子未被對方佔領,則向當前朝向移動一個單位(縱座標+1),否則保持不動;

. 開火指令F:己方坦克在當前回合立即向當前朝向開火;

己方坦克開火後,當前回合己方坦克的正前方若有對方的坦克,對方的坦克將被摧毀,遊戲結束,己方獲得勝利;若雙方的坦克在同一-回合被摧毀,遊戲結束,判定為平局;若雙方的坦克在同一回合內進入到同一個未被佔領的格子,則雙方的坦克發生碰撞,遊戲結束,判定為平局;當遊戲進行到第256個回合後,遊戲結束,若雙方坦克均未被摧毀,則佔領格子數多的一方獲得勝利,若雙方佔領的格子數一樣多,判定為平局。*注意, 若-方開火, 另-方移動,則認為是先開火,後移動。

現在小D和小W各自給出一串長度為256的指令字串, 請你幫助他們計算出遊戲將在多少個回合後結束,以及遊戲的結果。

輸入描述

輸入共兩行,每行為一串長度為256的指令寧符串,字串中只包含“U”,“D",“L" “R”,“F"這五個字元。第一行表示小D的指令,第工行表示小W的指令。

輸出描述

輸出一共兩行,第一行一個整數k,表示遊戲將在k個回合後結束。第二行為遊戲的結 果,若小D獲勝則輸出“D",若小W獲勝則輸出“W”若平局則輸出“P”

思路:
本題模擬坦克即可,考慮的情況挺多的,當時沒AC出來,後來也懶得做了

Coding 五

題目描述:

給一棵有n個點的有根樹,點的編號為1到n,根為1。每個點的顏色是紅色或者藍色。對於樹上的一個點,如果其子樹中(不包括該點本身)紅色點和藍色點的數量相同,那麼我們稱該點是平衡的。

請你計算給定的樹中有多少個點是平衡點。

輸入描述

第一行是一個正整數n,表示有n個點。

接下來行一個長度為n的字串,僅包含字元R’和’B', 第i個字元表示編號為的節點的顏色,字元為’R' 表示紅色,' B' 表示藍色。

接下來一行n-1個用空格隔開的整數,第1個整數表示編號為i+ 1的點的父親節點編號。1<=n<=10000

輸出描述

一行一個整數,表示樹上平衡點的個數。

思路:

根據題意,我們可以使用深度優先搜尋(DFS)來遍歷樹的每個節點,並計算出每個節點子樹中紅色和藍色節點的數量。

對於每個節點,我們可以將其子樹中紅色和藍色節點的數量儲存在節點的 cnt 屬性中。同時,我們也需要記錄該節點的父節點,以便在遍歷子樹時跳過父節點。

在DFS的過程中,我們可以計算出子樹中紅色和藍色節點的數量,並根據節點自身的顏色計算出該節點子樹中紅色和藍色節點的數量。然後我們將該節點的子節點作為新的起點進行DFS,直到遍歷完整棵樹。
對於每個節點,如果其子樹中紅色和藍色節點的數量相同,那麼它就是一個平衡點。
最後,我們可以將平衡點的數量相加,得到最終的結果。

type Node struct {
   color string
   cnt   [2]int
   child []*Node
}

func dfs(node *Node, parent *Node, cntRed int, cntBlue int) int {
   node.cnt[0] = cntRed
   node.cnt[1] = cntBlue
   res := 0
   for _, child := range node.child {
      if child == parent {
         continue
      }
      childCntRed := 0
      childCntBlue := 0
      if node.color == "R" {
         childCntRed = cntRed + 1
         childCntBlue = cntBlue
      } else {
         childCntRed = cntRed
         childCntBlue = cntBlue + 1
      }
      res += dfs(child, node, childCntRed, childCntBlue)
   }
   if node.cnt[0] == node.cnt[1] {
      res += 1
   }
   return res
}

func main() {
   scanner := bufio.NewScanner(os.Stdin)
   // 讀取輸入
   scanner.Scan()
   n := parseNum(scanner.Text())
   scanner.Scan()
   colors := strings.Split(scanner.Text(), "")
   nodes := make([]*Node, n+1)
   for i := 1; i <= n; i++ {
      nodes[i] = &Node{
         color: colors[i-1],
         cnt:   [2]int{},
         child: []*Node{},
      }
   }
   for i := 2; i <= n; i++ {
      scanner.Scan()
      parentIndex := parseNum(scanner.Text())
      nodes[parentIndex].child = append(nodes[parentIndex].child, nodes[i])
      nodes[i].child = append(nodes[i].child, nodes[parentIndex])
   }
   // DFS計算平衡點數量
   res := dfs(nodes[1], nil, 0, 0)
   // 輸出結果
   fmt.Println(res)
}

func parseNum(s string) int {
   var res int
   for _, c := range s {
      res = res*10 + int(c-'0')
   }
   return res
}

相關文章