P1036 选数
题意:简单来说就是给定n个数,从中选k个数字作为组合,判断所有这样组合的和并判断是否是素数,输出有多少个组合和是素数,输出个数。
方法一:
暴力递归+组合数筛选
我这样的暴力递归,是会重复Ak\k次,只需要除掉这么多次即可。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#define MOD 998244353
using namespace std;
typedef long long LL;
int arr[100];
bool isPrime(int num)
{/*不在6的倍数两侧的一定不是质数*/
if(num==2||num==3)
return 1;
/*不在6的倍数两侧的一定不是质数*/
if(num%6!=1&&num%6!=5)
return 0;
int tmp=sqrt(num);
/*在6的倍数两侧的也可能不是质数*/
for(int i=5;i<=tmp;i+=6)
if(num%i==0||num%(i+2)==0)
return 0;
/*排除所有,剩余的是质数*/
return 1;
}
bool flag[100];
int bns=0;
int ans=0;
int f(int n,int k,int s)
{
if(k==0)
{
if(isPrime(s))
{
ans++;
// cout<<s;
}
// cout<<s<<endl;
}
for(int i=1;i<=n;i++)
{
if(!flag[i])
{
flag[i]=1;
f(n,k-1,s+arr[i]);
flag[i]=0;
}
}
}
int F=1;
int main()
{
memset(flag,0,sizeof(flag));
int n,k;
cin>>n>>k;
for(int i=2;i<=k;i++)
{
F*=i;
}
for(int i=1;i<=n;i++)
cin>>arr[i];
f(n,k,0);
cout<<ans/F<<endl;
}
这个方法虽然是对的,但是重复了Ak\k次,时间为300ms。
方法二:
这个方法也是暴力递归,但是递归的过程中,按顺序选取,这样能保证不会重复。
节省了很多时间,为17ms.
#include<iostream>
#include<math.h>
using namespace std;
int x[20],n,k;//依照题目所设
bool isprime(int num)
{/*不在6的倍数两侧的一定不是质数*/
if(num==2||num==3)
return 1;
/*不在6的倍数两侧的一定不是质数*/
if(num%6!=1&&num%6!=5)
return 0;
int tmp=sqrt(num);
/*在6的倍数两侧的也可能不是质数*/
for(int i=5;i<=tmp;i+=6)
if(num%i==0||num%(i+2)==0)
return 0;
/*排除所有,剩余的是质数*/
return 1;
}
int rule(int choose_left_num,int already_sum,int start,int end){//choose_left_num为剩余的k,already_sum为前面累加的和,start和end为全组合剩下数字的选取范围;调用递归生成全组合,在过程中逐渐把K个数相加,当选取的数个数为0时,直接返回前面的累加和是否为质数即可
if(choose_left_num==0)return isprime(already_sum);
int sum=0;
for(int i=start;i<=end;i++){
sum+=rule(choose_left_num-1,already_sum+x[i],i+1,end);
}
return sum;
}
int main(){
cin>>n>>k;
for(int i =0;i<n;i++)cin>>x[i];
cout<<rule(k,0,0,n-1);//调用递归解决问题
}