華為OJ-素數伴侶-二分圖的最大匹配-匈牙利算法


題目描述

若兩個正整數的和為素數,則這兩個正整數稱之為“素數伴侶”,如2和5、6和13,它們能應用於通信加密。現在密碼學會請你設計一個程序,從已有的N(N為偶數)個正整數中挑選出若干對組成“素數伴侶”,挑選方案多種多樣,例如有4個正整數:2,5,6,13,如果將5和6分為一組中只能得到一組“素數伴侶”,而將2和5、6和13編組將得到兩組“素數伴侶”,能組成“素數伴侶”最多的方案稱為“最佳方案”,當然密碼學會希望你尋找出“最佳方案”。

輸入:

有一個正偶數N(N≤100),表示待挑選的自然數的個數。后面給出具體的數字,范圍為[2,30000]。

輸入描述:

輸入說明
1 輸入一個正偶數n
2 輸入n個整數

輸出:

輸出一個整數K,表示你求得的“最佳方案”組成“素數伴侶”的對數。

輸出描述:

求得的“最佳方案”組成“素數伴侶”的對數。

示例1

輸入

4
2 5 6 13

輸出

2

求解思路

  • 考慮素數伴侶的可能情況。若為兩個偶數或奇數,兩者和必然為偶數,不可能為素數,故唯一的組合是一個偶數和一個奇數的情況。
  • 根據數字的類型,可以將其分為奇數和偶數兩個集合,一個素數伴侶可以看做: 從奇數和偶數集合中各取出一個,連上一條邊。從而該問題轉化為二分圖的最大匹配問題。

編程關鍵點

  • 素性測試:判斷一個數是否為素數

    • 采用素數篩法。建立數組primes,首先將除2外的偶數標記為false,之后對奇數進行判斷, 判斷終止條件為\(\sqrt N\);為了避免重復判斷,已知\(p\)為素數,下次的判斷從\(p^2\)開始,將所有\(p\)的倍數置為false,由於\(p^2\)為奇數,奇數與偶數相乘為偶數, 為避免對偶數的重復判斷,\(p\)的倍數取值為:\(p^2 + 2ip,i=1,2,3...\)
    • 素數篩法求素數這篇文章對該方法進行了詳細的介紹,值得一讀;
    • 代碼如下:
      vector<int> primes(60001, 1);
      
      // generate prime numbers
      void prime_library() {
          primes[0] = primes[1] = 0;
          for (int i = 4; i < primes.size(); i += 2) {
              primes[i] = 0;
          }
          for (int i = 3; i * i < primes.size(); i += 2) {
              if (primes[i]) {
                  for (int j = i * i; j < primes.size(); j += 2 * i)
                      primes[j] = 0;
              }
          }
      }
      
  • 二分圖的最大匹配問題:匈牙利算法

    • 算法的詳細介紹可參考這篇文章二分圖的最大匹配、完美匹配和匈牙利算法, 我自己的理解是:
      • 數據被分為兩個集合:偶數和奇數。最開始所有的點都為未匹配點;
      • 從未匹配點出發,采用深度優先搜索,判斷是否能找到一個與之匹配的點;
      • 在DFS中,如果遇到了已經匹配的點,那么要從該匹配點向下繼續尋找,直到所有的點都匹配過。
    • 程序完整代碼如下:
      bool dfs(vector<int>& match, vector<int> & check, vector<int> &array, const unsigned x) {
          for (int i = 0; i < array.size(); ++i) {
              if (array[i] % 2 == 1 && primes[array[i] + array[x]] == 1 && check[i] == 0){
                  check[i] = 1;
                  if(match[i] == -1 || dfs(match, check, array, match[i])){
                      match[i] = x;
                      match[x] = i;
                      return true;
                  }
              }
          }
          return false;
      }
      
      int main() {
          int N;
          prime_library();
          while (cin >> N) {
              vector<int> array(N, 0);
              for (int i = 0; i < N; ++i) {
                  cin >> array[i];
              }
              int count{0};
              vector<int> match(N,-1);
              for (int i = 0; i < array.size(); ++i) {
                  //二分圖左側
                  if(array[i]%2 == 0){
                      vector<int> check(N,0);
                      check[i] = 1;
                      if(dfs(match, check, array, i))
                          ++count;
                  }
              }
              cout << count<<endl;
          }
          return 0;
      }
      
    • 備注:
    1. match被初始化為-1,它儲存的是素數伴侶的配對情況,如match[u] = v表示uv為一堆素數伴侶;
    2. check的初始化放在了內層循環,意味着每從偶數集合內取一個點,就要與奇數集合內所有的點做素數伴侶判斷,此時match數組是保留了之前的記錄信息的。


免責聲明!

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



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