試題請參見: https://vijos.org/p/1128
題目概述
已知 n 個整數 x1,x2,…,xn, 以及一個整數 k(k<n). 從 n 個整數中任選 k 個整數相加, 可分別得到一系列的和. 例如當 n=4, k=3, 4 個整數分別爲 3, 7, 12, 19 時, 可得全部的組合與它們的和爲:
3+7+12=22 3+7+19=29 7+12+19=38 3+12+19=34.
現在, 要求你計算出和爲素數共有多少個.
例如上例, 只有一種的和爲素數:3+7+19=29.
解題思路
很明顯需要用回溯的思路解決, 在n個數中選取k個數.
得到k個數後求和, 並判斷是否爲素數.
遇到的問題
解題過程中主要遇到兩個問題:
- 原題中敘述求
素數共有多少種
, 於是我使用set
去重. 然後妥妥的WA了. - 在遞歸時, 需要指定當前搜索開始的下標(代碼中的
searchIndex
), 否則結果會重複.
源代碼
#include <iostream>
#include <cmath>
const int MAX_N = 20;
int getSum(long long*, int, int);
void getSum(long long*, bool*, int, int, int, int, long long, int&);
bool isPrime(long long);
bool isAppeared(long long);
int main() {
int n = 0, k = 0;
long long numbers[MAX_N] = {0};
std::cin >> n >> k;
for ( int i = 0; i < n; ++ i ) {
std::cin >> numbers[i];
}
std::cout << getSum(numbers, n, k) << std::endl;
return 0;
}
/**
* 獲取不同的素數結果的個數.
* @param numbers n個數序列的集合
* @param n 序列的長度
* @param k 選取數的個數
* @return 不同的素數結果的個數
*/
int getSum(long long* numbers, int n, int k) {
int totalResults = 0;
bool isUsed[MAX_N] = {0};
getSum(numbers, isUsed, n, k, 0, 0, 0, totalResults);
return totalResults;
}
/**
* 回溯: 獲取n個數中選擇k個值的總和.
* @param numbers n個數序列的集合
* @param isUsed n個數的選擇情況(第i個數是否被選中)
* @param n 序列的長度
* @param k 選取數的個數
* @param searchIndex 下次搜索的下標
* @param selectedNumbers 當前選擇的個數
* @param sum 當前的總和
* @param totalResults 當前不重複的和的個數
*/
void getSum(long long* numbers, bool* isUsed, int n, int k, int searchIndex, int selectedNumbers, long long sum, int& totalResults) {
if ( selectedNumbers == k ) {
if ( isPrime(sum) ) {
++ totalResults;
}
}
for ( int i = searchIndex; i < n; ++ i ) {
if ( !isUsed[i] ) {
isUsed[i] = true;
getSum(numbers, isUsed, n, k, i, selectedNumbers + 1, sum + numbers[i], totalResults);
isUsed[i] = false;
}
}
}
/**
* 判斷一個數是否爲素數.
* @param x 待判斷的值
* @return 該數值是否爲素數
*/
bool isPrime(long long x) {
if ( x <= 2 ) {
return false;
}
for ( int i = 2; i <= std::sqrt(x); ++ i ) {
if ( x % i == 0 ) {
return false;
}
}
return true;
}