每日一題(20)——高效安排見面會

來源:互聯網
上載者:User

一、題目

在校園招聘的季節裡,為了能讓學生們更好地瞭解微軟亞洲研究院各研究組的情況,HR部門計劃為每一個研究組舉辦一次見面會,讓各 個研究組的員工能跟學生相互瞭解和交流。已知有n位學生,他們分別對m個研究組中的若干個感興趣。為了滿足所有學生的要求,HR希望每 個學生都能參加自己感興趣的所有見面會。如果每個見面會的時間為t,那麼,如何安排才能夠使得所有見面會的總時間最短? 最簡單的辦法,就是把m個研究組的見面會時間依次排開,那我們就要用m * t的總時間,我們有10多個研究小組,時間會拖得很長,能否進一步提高效率?

二、分析

        此題的官方解法是將問題轉化為一個已知的圖的問題:即圖的最少著色問題。 但有兩點感覺不太好:

  • 一是這個問題的轉換感覺角度有些大,不夠平滑。 如果此前沒聽過圖的最少著色問題,那麼是怎麼也不可能想到這兒的。
  • 二是關於此題的分析與解法講解非常籠統,尤其是解法,寥寥數語就完了 - 我覺得是沒有把問題講清楚的 - 雖然讀者自己可以閱讀“ 圖的最少著色問題”來獲得更多的瞭解,但既然此題作為單獨的一題存在,我覺得還是講清楚點好。

       另外,自己思考了一下,覺的此題不轉化成圖的最少著色問題,通過簡單的遞迴,應該也是可以實現的。

思路分析:(遞迴&合并的思想)

已知有n位學生,m個見面會,且每個學生可以選擇參加任意多個見面會。我們的目的是在沒有衝突的情況下把某幾個見面會的時間重疊起來,同時開。何為衝突,比如學生甲參加了見面會A與B,那麼A與B就是衝突的,因為如果同時開的話,學生甲必然要放棄一個。

那麼,我們可以按以下方式,逐個考慮見面會:

  • 對於見面會A,因為其前面沒有見面會,略過;
  • 對於見面會B,考慮它和其前面的見面會A是否衝突,如果不衝突,就將B和A合并,繼續考慮C;而另外還有一個分支是不管是否衝突,此時不做合并,直接考慮C;
  • 對於見面會C,考慮它和其前面的見面會A,B是否衝突,第一個分支是如果與A不衝突, 就將C和A合并,繼續考慮D;第二個分支是如果與B不衝突, 就將C和B合并,繼續考慮D;而第三格分支還是不做任何合并,直接考慮D
  • 按此規則繼續對下一個見面會做考察
  • 當對最後一個招聘會做完考察後,記下其時間,程式然後遞迴回溯,會由其他分支繼續考察最後一個招聘會,比較並記錄最短的那個,這樣,當所有的分支都考察過後,記錄的那個最短時間就是全域最短的時間了。 

 代碼

  • 輸入資料可以用一個二維數組input[m][n]來表示,行表示見面會,列表示學生,數組元素表示某學生是否參加該見面會。 
  • 遞迴過程用當前考慮的見面會控制,當最後一個見面會考慮完之後,就得到一個候選解,程式然後回溯,從另一分支再次進入,考慮最後一個見面會,與之前的候選解比較,儲存較優的那個:候選解包括見面會時間與當前的具體安排

複雜度分析

      可以注意到,我們需要逐個考慮見面會,一共是m個;而在考慮第i個見面會時,我們最多可能會產生出i個分支,不難看出,總問題的規模為m!;
而產生每個分支時需要做的計算是O(n)的衝突檢測與合并,於是,整個的演算法複雜度為:O(m! * n)

 

#include <iostream>#include <list> #include <vector>  using namespace std; // If 2 recruiting meetings are conflicting bool IsConflict(const vector<bool>& v1, const vector<bool>& v2) {     for(int i = 0; i < v1.size(); ++i)  {          if(v1[i] && v2[i]) return true;  }      return false;  }      // merge 2 recruiting meetings: v2 will be held at the same time of v1  void Merge(vector<bool>& v1, const vector<bool>& v2)  {      for(int i = 0; i < v1.size(); ++i)      {          v1[i] = v1[i] || v2[i];      }  }    // input: input[m][n], 2d array to represents students' selection of meetings.  // row stands for meetings, column stands for students,// and array value stands for if a student select a meeting  // check: The meeting to check  // curTime: Time required so far  // curArrangement: record the information of which meeting is merged with another  //meeting// best[output]:The best time  // bestArrangement[output] : The best arragement  void ArrangeRecruitings(vector<vector<bool> >& input, int check,             int& curTime, vector<list<int> >& curArrangement, int& bestTime,           vector<list<int> >& bestArrangement){       int m = input.size();      // base cases      if(check >= m)      {  // Save the best one  if(curTime < bestTime)          {                  bestTime = curTime;                  bestArrangement = curArrangement;          }  return;      }      else      {      // recursive cases  for(int i = 0; i <= check; ++i) // check: The meeting to check  {    if(curArrangement[i].empty()) continue; // if already merged with other ones, just skip it    if (!IsConflict(input[i], input[check]))  {    // update the status  vector<bool> bkI = input[i];  Merge(input[i], input[check]);    curArrangement[check].pop_back();  curArrangement[i].push_back(check);    // Consider check one after merge  ArrangeRecruitings(input, check+1, curTime, curArrangement, bestTime, bestArrangement);  // restore the status;bestarranyment has been saved  curArrangement[i].pop_back();  curArrangement[check].push_back(check);    input[i] = bkI;  }  }    // Consider check one without any merge  ArrangeRecruitings(input, check+1, ++curTime, curArrangement, bestTime, bestArrangement);    }  }      int main()  {      // 1. Initializing  int m = 3;  //meetingsint n = 4;   //students      vector<vector<bool> > input(m, vector<bool>(n, false));        input[0][0] = true;      //input[0][1] = true;        input[1][1] = true;      input[1][2] = true;        input[2][2] = true;      input[2][3] = true;     input[0][3] = true;          int curTime = 1;       int bestTime = m+1;       vector<list<int> > curArrangement(m);       vector<list<int> > bestArrangement(m);       for(int i = 0; i < m; ++i) curArrangement[i].push_back(i);          // 2. Solve       ArrangeRecruitings(input, 1, curTime, curArrangement, bestTime, bestArrangement);          // 3. Output the result       cout << "Totoal Time: " << bestTime << endl;       for(int i = 0; i < m; ++i)       {   cout << i << "): ";   if(bestArrangement[i].empty())   {   cout << "none" << endl;   continue;   }   for(list<int>::const_iterator it = bestArrangement[i].begin(); it != bestArrangement[i].end(); ++it)   cout << *it << "-";   cout << endl;     }   }   

 

三、第二種解決方案


       尋找活動集合S的最大相互相容集合的方法[2],在《演算法導論》貪心演算法一章中有詳細的講解。解決此問題的具體步驟可總結為:

        1)把面試集合S中的元素按其結束時間的遞增順序排列;

        2)求其最大相互相容面試集合M,並從原創組合S中刪除之;

        3)求集合S-M的最大相互相容面試集合Q,並從集合S-M中刪除之。重複此過程直到原始面試集合S中為空白集為止;

        4)統計求出的最大相互相容面試集合的個數,即是面試集合S所需的最少面試驗個數。

        以上面的面試集合S為例,上述步驟用映像表示如下:每個圖中用矩形框起來的面試為所屬集合的最大相互相容集合,一共有5個,即至少需要5個面試驗。

 

四、擴充問題一:

          研究院一天有N場面試召開(第i場面試:開始時間B[i],結束時間E[i]),每個應聘者只能參加一場面試,為了有一個好的面試環境,N場面試被安排在若干地點。不同的面試在同一時間不能被安排在同一地點。問至少需要多少個面試地點?

分析:

實際上轉化一下,就是求區間的最大重疊次數:即每個時間點的重疊次數,最後找到最大重疊次數即為所求。

書上P60的演算法,比較巧妙,但要注意的是:排序時要用到雙關鍵字比較,當兩個值相等時,屬於時間段開始的一定要排在屬於時間段結束的後面,只有這樣才能保證結果的正確性。(假設[3, 4)和[4, 5)能在同一個地方舉行。書上區間段都是用閉區間,本文採用前閉後開。)

考慮到面試安排的時間一般安排在某個整點、半點鐘或者某刻,可以採用計數的方法,如果都安排在整點,每處理一個區間[a, b),就對[a, b)間的所有整數計數一次。最後從計數結果中找出最大值即可。時間複雜度為O(n)(準確的講,應該是O(k*n),k為區間的最大間隔,k<=24)。如果面試安排時間在某個半點鐘、刻,可以對原來的時間乘以一個整數(比如2或4,這實際上就是桶排序設定桶間隔為0.5或0.25)。

//arr[][0]為面試開始時間,arr[][1]為面試結束時間int max_places (int arr[][2], size_t sz){  if (arr==NULL || sz<1) return 0;   const size_t MAX_HOURS=24;  int count[MAX_HOURS]={0};   int max=0, j=0;   size_t i=0;  for (i=0; i<sz; ++i)    for (j=arr[i][0]; j<arr[i][1]; ++j) ++count[j];//前閉後開區間  for (i=0; i<MAX_HOURS; ++i)    if (count[i]>max) max=count[i];  return max;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.