原文 https://www.facebook.com/notes/facebook-hacker-cup/qualification-round-solutions/598486173500621
今年的預選賽已經在1月29日結束了,總共有10169名選手成功解決了至少一道問題。來自密歇根大學的Mark在50分鍾內解決了全部三道問題,預選賽排名第一。預賽排名中的前500名選手獲得了進入下一輪比賽的資格。
第一題 美麗的字符串 (20分)
對於一個字符串,定義這個字符串的“美麗程度”是其所有字母“美麗程度”的總和(sum)。
每個字母都有一個“美麗程度”,范圍在1到26之間。沒有任何兩個字母擁有相同的“美麗程度”。字母忽略大小寫。
給出一個字符串,計算它最大可能的“美麗程度”。
英文原文 : http://pastebin.com/raw.php?i=RwGxZWsz
官方答案:
這是本輪比賽最簡單的題目。一共有10697名參賽者嘗試解決此題,其中9865名參賽者成功解決。解法的核心思想是:計算每個字母出現的頻率,給頻率最多的字母賦予“美麗程度值”26,以此類推。如果兩個字母頻率相等,可以任意挑一個賦予稍高的值,因為不影響字符串總和。
from collections import Counter def get_beauty(string): string = string.lower() # Remove all characters other than letters string = ''.join(x for x in string if 'a' <= x <= 'z' ) # Make a dictionary where the keys are letters and the values are counts freq = Counter(string) # Get the values (letter counts) and sort them in descending order arr = freq.values() arr.sort() arr.reverse() # 26 * (count of most common letter) + (25 * next most common) + ... values_and_counts = zip(range(26, 0, -1), arr) return sum(value * count for value, count in values_and_counts)
第二題 平衡的笑臉符號 (35分)
有時候我們把笑臉符號 :) 放在了括號中間,這時就比較難分辨到底是笑臉符號還是括號的一部分。
一段文本被視為“括號平衡” 需要滿足以下中的一個條件:
- 這段文本為空
- 有超過一個的英文字母,空格或者冒號
- 一個左括號,然后是一段“括號平衡”文本,接着一個右括號
- 一段“括號平衡”文本,接着又是一段“括號平衡”文本
- 一個笑臉符號 :) 或者哭臉符號 :(
給出一段文本,判斷它是不是“括號平衡”的。
英文原文 http://pastebin.com/raw.php?i=4SHerCEz
官方答案:
一共有7096名參賽者嘗試解決這道題,其中只有2086名參賽者成功解決。 對於這道題,有很多種方法。你可以用“暴力搜索”,動態規划+緩存,或者本文介紹的O(N)的算法。我們決定讓每位參賽者只要答案正確就算成功解決此題,因此上面的任何做法都可以。 下面介紹O(N)的算法。
核心思想是跟蹤“開括號”(指缺相應的右括號)數的范圍。
我們使用兩個變量,minOpen和maxOpen,都初始化為0。
逐個字符遍歷整段文本。
當遇見左括號時,maxOpen加一。如果這個左括號不是哭臉符號的一部分,minOpen也要加一。
當遇見右括號時,minOpen減一。如果這個右括號不是笑臉符號的一部分,maxOpen也要減一。 如果minOpen被減成負數了,重新令它為0。
最后,如果maxOpen被減成負數了,或者minOpen不為0,此時這段文本不可能是“括號平衡的”;反之,則是“括號平衡的”。
def isBalanced(message): minOpen = 0 maxOpen = 0 for i in xrange(len(message)): if message[i] == '(': maxOpen += 1 if i != 0 and message[i-1] != ':': minOpen += 1 elif message[i] == ')': minOpen = max(0, minOpen-1) if i != 0 and message[i-1] != ':': maxOpen -= 1 if maxOpen < 0: break if maxOpen >= 0 and minOpen == 0: return "YES" else: return "NO"
第三題 找最小 (45分)
有一個下標從0開始的數組M,里面有N個非負數。只有前K個數已知。
我們只知道,對於下標i的數,當 K <= i < N時,M[i]是前K個數中沒有包含的最小的非負數。
例如,如果K=3, N=4, 前K個數是[2, 3, 0],我們就能推出 M[3] = 1。
給出一個數組M中的前K個數,你的任務是推出這個數組中最后一個數M[N-1]。
另外,我們使用下列公式產生前K個數:
M[0] = A
M[i] = (B * M[i - 1] + C) % r, 0 < i < K
你應該在你的程序中根據輸入數據提供的A,B,C和r,自行生成前K個數。
英文原文 http://pastebin.com/raw.php?i=zupdZnkQ
官方答案:
總共有2555名參賽者嘗試解決這道題,總共有1929名參賽者成功解決。 這道題的難點在於測試數據中 N 的值會非常大。 不管這樣,你應該需要推斷出這兩點:
(1) M[i] (i >=k) 的最大值不可能大於 k+1。所以即使前面K個數的值最大可以到10^9,最終答案也不可能大於k+1。
(2) 根據上一點,M的后半段的值將在每k+1個數后重復。所以即使N很大,我們也只需要計算k+1個數,
現在我們把問題縮小到了找 m[k], m[k+1] ... m[2k+1]這些數。“暴力搜索”依然很慢,復雜度是O(K^2)。所以我們考慮使用一個BST(c++中用set/map)維持那些沒有在前K個數中出現的數。這樣復雜度就降低到了O(k log k),可以通過測試數據了。
下面是本次比賽第一名的這道題的代碼:
#include <iostream> #include <vector> #include <algorithm> #include <cstring> #include <map> #include <set> #include <queue> using namespace std; int M[200020]; int main() { int N; cin >> N; for(int t = 0; t < N; t++) { int n, k; cin >> n >> k; n--; int a, b, c, r; cin >> a >> b >> c >> r; M[0] = a; for(int i = 1; i < k; i++) { M[i] = (1ll * b * M[i - 1] + c) % r; LZ不是寫C的,順便求教一下,1LL是把1轉化為long的嗎? 乘以1LL又有什么用?
} set<int> st; for(int i = 0; i <= k; i++) st.insert(i); for(int i = 0; i < k; i++) st.erase(M[i]); multiset<int> dupst; for(int i = 0; i < k; i++) dupst.insert(M[i]); for(int i = k; i <= 2 * k; i++) { M[i] = *st.begin(); st.erase(st.begin()); if(i < 2 * k) { dupst.erase(dupst.find(M[i - k])); if(M[i - k] <= k && dupst.find(M[i - k]) == dupst.end()) { st.insert(M[i - k]); } } } cout << "Case #" << (t + 1) << ": "; if(n <= 2 * k) { cout << M[n] << endl; } else { cout << M[k + (n - 2 * k - 1) % (k + 1)] << endl; } } return 0; }
如果你喜歡這篇文章,請點擊右下方的“推薦”按鈕,謝謝 :)