审题
分析
题目要求我们从要求的数中取出几个数并且使其和为质数,乍一看分析起来有点难,因为取数的方法可以有很多种,但是进一步分析,好像和我们以前做过的全排列题目有些类似,但是全排列题目要求是求出某个数以内所有排列,转念一想,和这题是不是有点类似
那么就可将这题转换为全排列,那么我们是不是可以通过全排列来确定选数,比如题目中3 7 19可以组成素数29,那么我们是不是可以认为下标为0 1 3的数字可以组成素数,这样我们就可以通过全排列时候的下标索取,来获取不同组合的和
同时我们又要确定某个数是不是素数,这就有很多方法,我用的是比较朴素的O(sqrt(n))复杂度的方法(因为比较简单~),其他有改进的方法肯定更好
那么当我们交的时候,是不是就正确了呢?答案是不是的
还是以样例为例子,0 1 3可以组成素数,1 0 3是不是也可以呢?那么问题就来了:如何保证去重?
有些人会想到用set来去重,再输出set.size(),但是比如29可以是3 7 19组成,也可以是4 6 19组成,这样的话,应该有两种组合方式的情况会被缩减到一种
解决方法:对于n的全排列,我们发现由同一数字组成的重复的个数是n!,比如例题,由013组成的排列可以有3!=6种,那么我们可以写一个求阶乘的函数来解决这个问题
综上,该问题就圆满结束了,下面是实现代码
实现代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=30;
int n,num,cnt;
bool st[N];
int path[N],nm[N];
int di(int x){
if(x==1||x==0) return x;
return x*di(x-1);
}
bool is_primes(int x){
for(int i=2;i<=x/i;i++)
if(x%i==0) return false;
return true;
}
void dfs(int u){
if(u==num) {
int sum=0;
for(int i=0;i<num;i++)
sum+=nm[path[i]];
if(is_primes(sum))
cnt++;
}
for(int i=0;i<n;i++){
if(!st[i]){
path[u]=i;
st[i]=true;
dfs(u+1);
st[i]=false;
}
}
}
signed main(){
cin>>n>>num;
for(int i=0;i<n;i++) cin>>nm[i];
dfs(0);
cout<<cnt/di(num);
return 0;
}