集合是指由一个或多个确定的元素所构成的整体,也可以当作不分顺序的数组
当集合中不包含任何元素时,我们称它为空集
我们一般用大括号及期中若干元素表示一个集合,例如
1
,
3
,
5
{1,3,5}
1,3,5表示包含元素1,3,5的一个集合,
a
,
x
,
a
b
c
{a,x,abc}
a,x,abc表示包含三个字符串元素的集合
在集合的元素中没有先后顺序,例如集合
1
,
2
,
3
{1,2,3}
1,2,3和{3,1,2}是等价的
在集合中,有一些集合间的关系,我们这种送会用到一个关系是子集,我们说集合A是集合B的子集,表示A钟所有元素都在B中出现(A集合可以是空集)
例如,对于{1,2,3,4,5},{1,3,5}、{2,5}、{4}都是它的子集,而{1,4,6}不是它的子集
一个长度为n的集合,一共有
2
n
2
2n^2
2n2个子集
对于集合A,可以用一个二进制数来表示集合A的某个子集B。我们用二进制的每一位表示集合A中的每个元素是否在子集B中出现,1表示出现,0表示未出现
对于集合{0,1,2,3,4,5,6}那么二进制
(
0101101
)
2
(0101101)_2
(0101101)2就表示子集{0,2,3,5}
二进制数位 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
{二进制数值 | 0 | 1 | 0 | 1 | 1 | 0 | 1 |
选取的元素 | - | 5 | - | 3 | 2 | - | 0 |
当然,我们还有各种各样的方法来表示子集,但由于计算机内部位运算的便捷性,我们通常借助二进制和位运算完成集合的表示和运算。
例题1:得到整数X
对于 n 个互不相同的正整数,要从这 n 个正整数之中无重复地选取任意个数,使得选出的数的总和为 X。现在你需要求出一共有多少种不同的选取。
对于这道题,我们就可以先枚举这n个数组成的集合的子集,把所有可能的二进制一次枚举,然后根据枚举的二进制对应的子集,从而求出该子集中所有元素的和:
1<<n表示
2
n
2^n
2n
KaTeX parse error: Expected 'EOF', got '&' at position 2: i&̲(1<<j)表示i中的第j位是否为1
#include <iostream>
using namespace std;
int main() {
int n, x, ans = 0, a[30];
cin >> n >> x;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for(int i=0;i<(1<<n);i++){
int num=0;
for(int j=0;j<n;j++){
if(i&(1<<j)){
num+=a[j];
}
}
if(num==x){
ans++;
}
}
cout<<ans<<endl;
return 0;
}
例题2:李白打酒
话说大诗人李白,一生好饮。
一天,他提着酒壶,从家里出来,酒壶中有酒两斗。他边走边唱:
- 无事街上走,提壶去打酒。
- 逢店加一倍,遇花喝一斗。
这一路上,他一共遇到店 55 次,遇到花 1010 次,已知最后一次遇到的是花,他正好把酒喝光了。请你计算李白有多少种满足要求的遇到店和花的可能情况。
我们已知遇到点5次,遇到花100次,并且最后一次遇到花,正好把酒喝光。我们可以用01串来表示李白一次遇到的是店还是花,用1表示店,用0表示花。
因为已经确定最后一次遇到的是花,所有我们只需要枚举前14次分别是店还是花,根据枚举出的14位01串,判断是否确保刚好有5个1和9个0,并且最后刚好剩一斗酒
#include <iostream>
using namespace std;
int main() {
int ans = 0;
for (int i = 0; i < (1 << 14); ++i) {
int tot_1 = 0;
int tot_0 = 0;
int num = 2;
for(int j=0;j<14;j++){
if(i&(1<<j)){
tot_1++;
num=num*2;
}
else{
tot_0++;
num=num-1;
}
}
if(tot_1==5 && tot_0==9 && num==1){
ans++;
}
}
cout << ans << endl;
return 0;
}