題目描述
若兩個正整數的和為素數,則這兩個正整數稱之為“素數伴侶”,如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; }
- 備注:
match
被初始化為-1,它儲存的是素數伴侶的配對情況,如match[u] = v
表示u
和v
為一堆素數伴侶;check
的初始化放在了內層循環,意味着每從偶數集合內取一個點,就要與奇數集合內所有的點做素數伴侶判斷,此時match
數組是保留了之前的記錄信息的。
- 算法的詳細介紹可參考這篇文章二分圖的最大匹配、完美匹配和匈牙利算法, 我自己的理解是: