[问题描述]:
已知 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 (1<=n<=20,k<n)
x1,x2,…,xn (1<=xi<=5000000)
[输出]:
屏幕输出,格式为:
一个整数(满足条件的种数)。
[输入输出样例]:
输入:
4 3
3 7 12 19
输出:
1
问题分析:
本题可以分解成以下两部分:
1. 从n个数中任取k的组合求法;
2. 判断一个整数是否是素数。
我们就这两个部分分别进行讨论。
(1)从n个数中任取k个数的组合,即C(n,k),(k
由于题目给定 n<=20,因此可以通过搜索实现。穷举和深度优先搜索dfs是通常采用的两种方法。
穷举
用数组a[1],a[2],….a[k]记录每种组合中各数的下标。a[0]为结束条件。
初值: 1 2 。。。。。 k-1 k
下一个: 1 2 。。。。。k-1 k+1
….. … … …… ….
直到: 1 2 。。。。。k-1 n
。。。 。。。 。。。。 。。。
最后一个组合:
n-k+1 n-k+2 ….. n-1 n
算法描述:
while(a[0]==0)
{
求和;
判断素数;
j=k;
while(a[j]==n-k+j)
j--;
a[j]=a[j]+1;
for(i=j+1;i<=k;i++)
a[i]=a[i-1]+1;
}
当选用前面的输入样例,n=4,k=3时,a[1]~a[3]的变化如下:
a[0] a[1] a[2] a[3] 生成的组合
0 1 2 3 3 7 12
0 1 2 4 3 7 19
0 1 3 4 3 12 19
0 2 3 4 7 12 19
1 2 3 4 循环结束
简单dfs:
可用递归搜索来实现(用S表示各组合中的数据之和,用x[i]记录各原始数据)
void search(dep) //设定第dep个数据的值
{
for(i=a[dep-1]+1;i<=n-k+dep;i++)
{
a[dep]=i;
s+=x[i];
if(dep
else 加和并判断素数
}
}
(2)判断一个整数是否为素数
本题的数据范围比较大,快速判断一个整数为素数则是提高速度的关键。这里我采用筛法求素数(详见拙作http://blog.csdn.net/fisher_jiang/archive/2006/05/11/724956.aspx),至于素数测试的概率算法,离作以后研究吧。
素数的筛法:对于一个大整数X,只要知道不超过 sqrt(X)的所有素数,划去所有p的倍数2p,3p,...剩下的整数就是不超过X的全部素数
我们这里设y=int(sqrt(s))+1,.s为待判断的整数。如果在2~y中不存在素数i为s的约数 ,则 s 为素数。由于xi<=5000000,n<=20,因此s<=100000000,y<=10000.而2~10000之间共有1229个素数,如果预先将这1229个数据存储到素数集合中,超时的可能性大大降低。
/*f[10001];//存储1-10000之间数据的状态,f[i]=1表示i为素数,0表示i为非素数*/
void Get_Prime()//筛选法求1-10000之间的素数
{
memset(p,0,sizeof(p));
int i,j,s;
s=0;
f[1]=0;
for(i=2;i<=10000;i++)
f[i]=1;
for(i=2;i<=10000;i++)
if(f[i]==1)
{
s++;
p[s]=i;
j=2*i;
while(j<=10000)
{
f[j]=0;
j+=i;
}
}
}
下面是采用穷举法的例程:
#include
#include
using namespace std;
int p[1230]; //存储10000以内的素数
long x[21];//存储原始数列
int a[21];//存储k个数的选择状态:a[i]=j表示选择的第i个数在原始数列中的下标为j;
long sum;//累加器,统计k个数的和
long total;//统计满足要求的组合个数
int f[10001];//存储1-10000之间数据的状态,f[i]=1表示i为素数,0表示i为非素数
int n,k;
void Get_Prime()//筛选法求1-10000之间的素数
{
memset(p,0,sizeof(p));
int i,j,s;
s=0;
f[1]=0;
for(i=2;i<=10000;i++)
f[i]=1;
for(i=2;i<=10000;i++)
if(f[i]==1)
{
s++;
p[s]=i;
j=2*i;
while(j<=10000)
{
f[j]=0;
j+=i;
}
}
}
bool fit(long sum)
{
int m,Max;
Max=int(sqrt(sum+0.0))+1;//开始没有+1,测试数据木有通过
m=1;
while(sum%p[m]!=0&&p[m]
m++;
if(p[m]>=Max)
return 1;
else
return 0;
}
int main()
{
int i,j;
Get_Prime();
while(cin>>n>>k)
{
for(i=1;i<=n;i++)
cin>>x[i];
total=0;
for(i=0;i<=k;i++)
a[i]=i;
while(a[0]==0)
{
sum=0;
for(i=1;i<=k;i++)
sum+=x[a[i]];
if(fit(sum))
total++;
j=k;
while(a[j]==n-k+j)
j--;
a[j]=a[j]+1;
for(i=j+1;i<=k;i++)
a[i]=a[i-1]+1;
}
cout<
}
return 0;
}