P1036 [NOIP 2002 普及组] 选数

题目描述

已知 n 个整数 x1​,x2​,⋯,xn​,以及 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≤n≤20,k<n)。

第二行 n 个整数,分别为 x1​,x2​,⋯,xn​(1≤xi​≤5×106)。

输出格式

输出一个整数,表示种类数。

输入输出样例

输入 

4 3
3 7 12 19

输出 

1

代码

无注释版 

#include<bits/stdc++.h>
using namespace std;
int prime(int x){
	for(int i=2;i<=sqrt(x);i++){
		if(x%i==0) return 0;
	}
	return 1;
}
int a[22],f[22];
int s,sum;
int n,k;
void dfs(int x,int h){
	if(x==k){
		if(prime(s)){
			sum++;
		}
		return;
	}
	for(int i=h;i<=n;i++){
		if(f[i]==0){
			f[i]=1;
			s+=a[i];
			dfs(x+1,i+1);
			s-=a[i];
			f[i]=0;
		}
	}
}
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	dfs(0,1);
	cout<<sum;
}

有注释版

#include<bits/stdc++.h>  // 引入标准库,包含常用的C++库
using namespace std;  // 使用标准命名空间

// 判断一个数是否为素数
int prime(int x) {
    // 如果 x 小于 2,直接返回 false
    if (x < 2) return 0;
    
    // 从 2 到 sqrt(x) 遍历,检查是否存在能整除 x 的数
    for (int i = 2; i <= sqrt(x); i++) {
        if (x % i == 0) return 0;  // 如果能被 i 整除,则 x 不是素数
    }
    return 1;  // 否则 x 是素数
}

int a[22], f[22];  // a 数组存储输入的整数,f 数组用于标记数字是否被选择
int s, sum;  // s 用来存储当前组合的和,sum 用来存储符合条件的素数和的组合数
int n, k;  // n 是整数个数,k 是要选择的整数个数

// 深度优先搜索(DFS)函数,用来遍历所有选择 k 个数的组合
void dfs(int x, int h) {
    // 如果已经选择了 k 个数
    if (x == k) {
        // 如果当前和是素数,则计数
        if (prime(s)) {
            sum++;  // 累加符合条件的组合
        }
        return;  // 返回,不再继续选择更多数
    }
    
    // 遍历从 h 到 n 位置的整数,选择未被选择过的数
    for (int i = h; i <= n; i++) {
        if (f[i] == 0) {  // 如果第 i 个数没有被选择过
            f[i] = 1;  // 标记该数为已选择
            s += a[i];  // 将该数加入当前组合的和中
            dfs(x + 1, i + 1);  // 递归选择下一个数,并将当前位置 i+1 作为下次的起始位置
            s -= a[i];  // 回溯,撤销选择,恢复当前和
            f[i] = 0;  // 标记该数为未选择
        }
    }
}

int main() {
    cin >> n >> k;  // 输入 n 和 k
    
    // 输入 n 个整数
    for (int i = 1; i <= n; i++) {
        cin >> a[i];  // 读入整数并存入数组 a
    }
    
    // 从第一个位置开始,进行深度优先搜索
    dfs(0, 1);
    
    // 输出符合条件的素数和的组合数
    cout << sum;
}
### NOIP2002 普及组 P1036 Python 解题思路 对于给定的自然集合,从中取若干个不同的元素组成子集,使得这些元素之和能被k整除。此问题可以通过回溯法来解决,在遍历过程中记录当前路径上的总和并判断其能否被k整除。 当遇到符合条件的情况时,则找到了一组满足条件的组合;反之则继续探索其他可能性直到完成整个搜索空间的遍历。为了提高效率,可以在每次递归调用前先检查剩余未访问节点的量是否足以构成新的合法解——如果不足,则提前终止该分支下的进一步尝试[^1]。 具体来说: - 使用列表`nums`存储输入据; - 定义辅助函`dfs(index, current_sum)`用于执行深度优先搜索; - `index`表示正在考虑加入子集中的下一个位置; - `current_sum`代表目前累积起来达到的部分和; - 如果`current_sum % k == 0`且不为空集,则找到一种方案; - 否则依次尝试将第`index`位之后每一个字纳入考量范围之内,并更新相应的参值传递给下一层级直至结束。 下面是完整的Python代码实现: ```python def select_numbers(nums, k): result = [] def dfs(index, path, current_sum): nonlocal result # 当部分和可被k整除且不是空集时保存结果 if index >= len(nums) or (len(path)>0 and current_sum % k == 0): if len(path) > 0: result.append(list(path)) for i in range(index, len(nums)): # 尝试把当前位置的值加进来 path.append(nums[i]) # 继续向下一层迭代 dfs(i + 1, path, current_sum + nums[i]) # 回退操作移除最后一个添加进去的项以便测试其它可能的择 path.pop() # 开始从第一个元素处进行深搜 dfs(0,[],0) return result if __name__ == "__main__": n,k=map(int,input().split()) numbers=list(map(int,input().strip().split())) res=select_numbers(numbers[:n],k) print(len(res)) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值