从n个数字选取k个数进行组合且符合升序。

本文介绍了一种算法,用于从n个数字中选取k个进行字典序升序排列组合。通过迭代和位操作,找到并输出所有可能的组合。代码包括带注释和不带注释的版本,并提供了测试用例展示输出效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

🎴 从n个数字选取k个数进行组合,升序。


从n个数字选取k个数进行排列组合,且符合字典序升序。

一句话解释:

设comb初值如上 ,即 comb = 1 << k -1;

重复:

  1. 找到最低位的 01 \large 01 01 ,其中 0 \large0 0变为 1 \large1 1
  2. 将其右边部分右移(不包括该 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];
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值