🎴 从n个数字选取k个数进行组合,升序。
从n个数字选取k个数进行排列组合,且符合字典序升序。
一句话解释:
设comb初值如上 ,即 comb = 1 << k -1;
重复:
- 找到最低位的 01 \large 01 01 ,其中 0 \large0 0变为 1 \large1 1。
- 将其右边部分右移(不包括该 1 \large1 1),直到右边部分少一个 1 \large1 1(保证了数字 1 \large1 1的数量不变,且符合字典序升序)。
代码
带注释版本
int n,k;
cin >> n >> k;
int comb = 1 << k - 1;
while(com < 1 << n){
// x 表示当前comb最低位的1
// 例: comb = 0101 1100,-comb=1010 0100,则有 x=0000 0100。
int x = comb & -comb;
// y 将"可变0"变成"1"(接下来提到的所有"1"也是这个"1"),且"1"左边的数字保留(包括"1"),剩下只需处理"1"右边的数字。
//例: comb = 0101 1100,x=0000 0100,则有 y=0110 0000。
int y = comb + x;
// z表示原comb"可变0"右边的数字(不包括"可变0")。
//例:comb=0101 1100,,~y=1001 1111,则有z=0001 1100
int z = comb & ~y;
//表达式逻辑顺序:先除x,再移位。
// z a经comb中"可变0"右边部分(不包括"可变0")进行右移直到消失一个1,因为左边1的个数比原来多了一个,所以右边要消失一个1。
// z = 0000 0011;
z = z / x >> 1;
// 进行或操作,将左右两边进行"合并"。
// 左边 y = 0110 0000 ,z = 0000 0011 ,
comb= z | y;
}
不带注释版本
int n,k;
cin >> n >> k;
int comb = 1 << k - 1;
while(com < 1 << n){
int x = comb & -comb;
int y = comb + x;
int z = (comb & ~y) / x >> 1;
}
效果
测试代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<set>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
int main() {
int n,k;
cin >> n >> k;
ll comb = (1 << k) -1;
while(comb < 1 << n){
bitset<8> bs(comb);
cout << bs <<"\n";
int x = comb & -comb, y = comb + x;
comb = ((comb & ~y) / x >> 1) | y;
}
}
输入
5 3
输出
00111
01011
01101
01110
10011
10101
10110
11001
11010
11100
应用的时候,只需对某一个组合,查询第i位是否为1。
如00111,表示第1、2、3位进行组合。
简单组合相加代码:
int sum(int n,int comb,int nums[]){
int ans=0;
for(int i=0;i<n;i++){
if((1 << i) & comb)
ans+=nums[i];
}
}