P1036 [NOIP2002 普及组] 选数

[NOIP2002 普及组] 选数

题目描述

已知 n 个整数 x1,x2,,xn,以及 1 个整数 kk<n)。从 n 个整数中任选 k 个整数相加,可分别得到一系列的和。例如当 n=4k=34 个整数分别为 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,k1n20k<n)。

第二行 n 个整数,分别为 x1,x2,,xn1xi5×106)。

输出格式

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

样例 #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 @   DawnTraveler  阅读(111)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示