普及-每日一题洛谷P1036
题目描述
已知 \(n\) 个整数 \(x_1,x_2,\cdots,x_n\),以及 \(1\) 个整数 \(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 \le n \le 20\),\(k<n\))。
第二行 \(n\) 个整数,分别为 \(x_1,x_2,\cdots,x_n\)(\(1 \le x_i \le 5\times 10^6\))。
输出格式
输出一个整数,表示种类数。
样例输入
4 3
3 7 12 19
样例输出
1
经典的DFS搜索问题,从 \(n\) 个数中选出 \(k\) 个数,使他们的和为素数,计算素数的个数
因为每个数只能选一次,而且计算的是他们的和,所以选数的顺序没有区别
就要做到不重不漏,我们每次搜索下一个数时,就要从前一个数之后开始寻找:
void dfs(int x,int st){//x表示当前我们考虑的是第几个组合数,st表示这次选数从多少开始选if (x>k){//当我们考虑到第k个组合数之后时,触碰到边界返回if (prime(sum))//如果是素数,答案加1ans++;return ;}for (int i=st;i<=n;i++){//从第st个数开始往后查找sum+=a[i];//计算组合数之和dfs(x+1,i+1);//递归查找第x+1个组合数sum-=a[i];//回溯复位}
}
因为每次都从上一个组合数之后的数开始查找,我们选出来的数构成了一个下标严格单调递增的序列
该序列的元素不会重复,即不会回头选我们前面已经选过了的数,组合方案有 \(C_k^n\) 个
完整代码:
#include <stdio.h>int n,k,sum,ans;
int a[22];bool prime(int x){if (x==2) return 1;if (x%2==0) return 0;for (int i=2;i<=x/i;i++)if (x%i==0) return 0;return 1;
}void dfs(int x,int st){if (x>k){if (prime(sum))ans++;return ;}for (int i=st;i<=n;i++){sum+=a[i];dfs(x+1,i+1);sum-=a[i];}
}int main()
{scanf("%d %d",&n,&k);for (int i=1;i<=n;i++) scanf("%d",&a[i]);dfs(1,1);printf("%d",ans);return 0;
}