P1036——选数

题目描述:
已知 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)

输出格式:

屏幕输出,格式为:
一个整数(满足条件的种数)。

输入输出样例

输入样例#1:

4 3
3 7 12 19

输出样例#1:

1

思路:
找到这些组合,显然是一个搜索问题。如果直接用DFS方法进行搜索,会导致出现组合重复排列的情况。所以每一次的搜索初始位置都比上一次加一。代码如下:

#include<iostream>
#include<cmath>
const int MAX = 30;
using namespace std;
int n, k;//数的总数和选取的数的总数
int _count = 0;//the total number of prime numbers
int sum = 0;//记录搜索时相加的和
int a[MAX];///记录数据
//a function of prime numbers
bool isPrime(int x) {
    if (x <= 1)
        return false;
    else if (2 == x)
        return true;
    else {
        for (int i = 2; i <= sqrt(x); i++)
            if (0 == x % i)
                return false;
    }
    return true;
}
//DFS搜索函数
/*这里我仔细讲解一下DFS,level表示的递归层数,在后面主函数调用时从零开始,starti表示搜搜索的初始位置。
我们先看递归窗口。若果starti>n-1且此时的层数还没有达到k,再往下搜索就越界了,所以我们return。但是如果在还没有越界的时候层数就达到k,那么我们就判断累加和sum是否为素数。是的话计数器加一。然后在return。
最后我们看一看主体部分。搜索,就要把每种组合都找出来。我们用循环,从starti到n,不断变换初始搜索位置。当starti是搜索的初始位置时,sum加上a[starti],然后继续递归搜索,递归层数加一,但要注意,为了防止重复,我们把初始搜索位置也加一,这样不需要用vis数组记录是否访问。
最后,要释放sum,也就是减去a[j]即可
*/
void DFS(int a[MAX], int level, int starti) {
    if (starti > n - 1 && level < k)
        return;
    if (level == k) {
        if (isPrime(sum))
            _count++;
        return;
    }
    for (int j = starti; j < n; j++) {
        sum += a[j];
        DFS(a, level + 1, j + 1);
        sum -= a[j];
    }
}
int main()
{
    cin >> n >> k;
    for (int i = 0; i < n; i++)
        cin >> a[i];
    DFS(a, 0, 0);
    cout << _count;
    return 0;
}




### 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)) ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值