[譯] Facebook杯2013年編程挑戰賽——預選賽題目及答案


原文 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;
}

 

如果你喜歡這篇文章,請點擊右下方的“推薦”按鈕,謝謝 :)

 


免責聲明!

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



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