p1036选数
P1036 [NOIP2002 普及组] 选数
前言
感觉这道题挺不错,学习了两种不同的暴力枚举解法,所以整理了一下题解发在这里。
题目描述
已知 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。
解法1-子集枚举
解法思路
以下内容摘自 《洛谷深入浅出》,具体请看书本147页
本题可以认为是从一个有n个数字的集合中挑选出一些数字(也就是子集),然后判断该子集是否满足某个性质(其和是质数)。
集合枚举的意思是从一个集合中找出它的所有子集。本题利用二进制数来进行子集枚举。
我们用1表示元素的存在,0表示元素的不存在,然后列表(部分)表示一些集合:
A中元素 | 1 | 2 | 3 | 4 | 5 | 二进制 | 对应十进制 |
---|---|---|---|---|---|---|---|
在A1中的出现情况 | 1 | 0 | 1 | 1 | 1 | 11101 | a1=29 |
在A2中的出现情况 | 1 | 0 | 0 | 1 | 1 | 11001 | a2=25 |
在A3中的出现情况 | 0 | 0 | 1 | 0 | 0 | 00100 | a3=4 |
在A4中的出现情况 | 0 | 1 | 1 | 0 | 0 | 00110 | a4=6 |
从上表中我们可以发现二进制数和集合的联系。
同时我们通过位移运算来构造仅包含第i(1<=i<=5)个元素的集合的数字,即1<<(i-1)
,还能构造全集(1<<n)-1
,n表示元素个数。
一些集合的常用关系有以下几种:
- 并集:
a1 | a2
- 交集:
a1 & a2
- 包含:
(a1 | a2 == a1) &&(a1 & a2 == a2)
- 属于:
1 << (i-1) & a1
,i表示第几个元素 - 补集:
a^a1
,a表示全集,^为c++的异或运算
有了上述思路之后,我们就可以开始解题了。
代码实现
1 |
|
解法2-递归枚举
解法思路
该解法内容基本完全摘自 https://www.luogu.com.cn/article/tle6xrur
这题的难点是:如何去重?
答案是:不降原则
不降原则
举个例子:
比如说在6里面随便选5个数,那么选法都是什么呢?
瞎枚举?
12345
12346
前两个还不会弄混
然后很可能就乱了
少点数可能不会乱
但是多了就不好整了
比如说在100里随便选50个数。
1 2 3 4 5 6 7 8 9 10 11 12……
Die.
所以我们可以运用不降原则:
保证枚举的这些数是升序排列
其实真正的不降原则还可以平
比如 1 2 2 3 3 4……
但是请注意这道题也不能平
否则就有重复数字了
拿6个里面选3个举例子
1 2 3
1 2 4
1 2 5
1 2 6
第一轮枚举完毕。
第二个数加一
1 3 ?
这个“?”应该是4,因为是升序排列
1 3 4
1 3 5
1 3 6
接着,就是这样
1 4 5
1 4 6
1 5 6
第一位是1枚举完毕
第一位是2呢?
2 3 4
2 3 5
2 3 6
2 4 5
2 4 6
2 5 6
就是这样的,枚举还是蛮清晰的吧
以此类推…..
3 4 5
3 4 6
3 5 6
4 5 6
然后就枚举不了了,结束。
所以说,这样就可以避免判重了。
代码实现
1 |
|