P1036 [NOIP2002 普及组] 选数

[NOIP2002 普及组] 选数

题目描述

已知 \(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\))。

输出格式

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

样例 #1

样例输入 #1

4 3
3 7 12 19

样例输出 #1

1

提示

【题目来源】

NOIP 2002 普及组第二题

2.题解

2.1 子集枚举

思路

概念请参考子集枚举
难点集中在如何确定n个数中选取k个数的问题,这里将n个数的下标组成一个二进制数
通过函数__builtin_popcount(){注意该函数前面的下划线是两个,后面的下划线是一个!!!}可以判断一个二进制数有几位是1来判断是否满足选取了k个数。
至于范围选定就是从[0,11111...111]即可,如何快速获得n个1? (即 (1 << n) - 1, 可参考链接中的介绍)

代码

// 子集枚举 
#include<bits/stdc++.h>
using namespace std;

bool isPrime(int num){
	if (num <= 1) return false;
	if (num <= 3) return true;
	// 若num不是质数,num = a * b, a > sqrt(num),则必有 b < sqrt(num), 我们只要看能否找到这个b即可判断了
	for(int i = 2; i <= sqrt(num); i++){
		if(num % i == 0) return false;
	} 
	return true;
} 

int main(){
	int n, k;
	int ans = 0;
	cin >> n >> k;
	vector<int> arr(n);
	for(int i = 0; i < n; i++){
		scanf("%d", &arr[i]);
	}
	int U = 1 << n; // 得到全集,确定范围
	for(int S = 0; S < U; S++){ //枚举所有子集[0,U) 
		int sum = 0;
		if(__builtin_popcount(S) == k){
			for(int i = 0; i < n; i++){
				if(S & (1 << i)) sum += arr[i];
			}
			if (isPrime(sum)) ans++;
		}
	} 
	cout << ans;
}	
posted @ 2024-02-16 20:19  DawnTraveler  阅读(171)  评论(0)    收藏  举报