今天去嘗試了一下美團 AI 平臺,兩次面試連一起。但是兩位面試官小哥都是做推薦的,我們互相都不瞭解對方怎麼做的。於是乎就做算法題,講論文(把不懂的人講懂確實困難),然後全程小哥給我介紹他們部門情況,我就掛機聽着。不管這家拿不拿得到,就當刷刷經驗吧,也挺不錯的。一共三道題目,前兩道一個最長上升子序列,一道快速排序,就不講了,都是原題。
題目描述
題目鏈接:
牛客網:分石子[1]
牛牛有 n
堆石子堆,第 i
堆一共有 a[i]
個石子。
牛牛可以對任意一堆石子數量大於 1
的石子堆進行分裂操作,分裂成兩堆新的石子數量都大於等於 1
的石子堆。
現在牛牛需要通過分裂得到 m
堆石子,他想知道這 m
堆石子的最小值最大可以是多少?
示例:
輸入:
3,5,[3,5,6]
輸出:
2
解釋:
把5分裂成2和3
把6分裂成2和4
得到五堆石子[3,2,3,2,4]
備註:
- 第一個參數
n
代表石子堆的個數 - 第二個參數
m
表示需要得到的石子堆數。 - 第三個參數
vector a
代表每堆石子堆的石子個數
題解
一拿到這個題目,就看出來這是一道二分答案的題目。
首先定義上下界 l = 1, r = min{a[i]}
,也就是說,每一堆個數最小值至少爲 1
,最多就是初始的時候最小的那堆個數。
然後對於 mid = (l + r) / 2
,含義就是假設最終最小的那堆有 mid
個。我們求出初始時每一堆最多可以劃分出多少個數全部大於等於 mid
的子堆,顯然個數是 a[i] / mid
取整,記總堆數爲 cnt
。
如果 cnt < m
,那麼說明 mid
太大了,你最多也不可能劃分成 m
堆,所以更新 r = mid - 1
。如果 cnt > m
,那麼說明 mid
太小了,你能劃分的堆數大於了 m
,那麼更新 l = mid + 1
。最後如果 cnt = m
,你就暫存一下答案,因爲這時的 mid
是有可能成爲最終答案的。但是 mid
還是可能太小了,因爲 mid
稍微大一點 cnt
是不會變的,所以繼續更新 l = mid + 1
。
最終返回暫存的答案 res
即可。注意這題的二分框架和之前做過的有所不同,在等號判斷上得特別小心,我一開始沒想清楚,錯了好多次才通過的。
代碼
class Solution {
public:
/**
* 分石子
* @param n int整型
* @param m long長整型
* @param a int整型vector
* @return int整型
*/
int solve(int n, long long m, vector<int>& a) {
typedef long long ll;
ll l = 1, r = *min_element(a.begin(), a.end()), res = 0;
while (l <= r) {
ll mid = l + (r - l) / 2;
ll cnt = 0;
for (auto x : a) {
cnt += x / mid;
}
if (cnt < m) r = mid - 1;
else {
l = mid + 1;
res = mid;
}
}
return res;
}
};
參考資料
[1]
牛客網:分石子: https://www.nowcoder.com/questionTerminal/1ea5b4eaeff841a4918931791b000756