二进制-枚举子集

话说大诗人李白,一生好饮。幸好他从不开车。

一天,他提着酒壶,从家里出来,酒壶中有酒两斗。他边走边唱:

  • 无事街上走,提壶去打酒。
  • 逢店加一倍,遇花喝一斗。

这一路上,他一共遇到店 55 次,遇到花 10

10 次,已知最后一次遇到的是花,他正好把酒喝光了。请你计算李白遇到店和花的次序,有多少种可能的方案。

这个题目解法很多,二进制枚举是一种写起来非常简洁的解法。我们已知遇店 55 次,遇花 1010 次,并且最后一次遇到花,正好把酒喝光。那么我们可以把店作为二进制中的 11,把花作为二进制中的 00,因为已经确定最后一次遇到的是花,所以我们需要判断枚举的结果是否刚好有 55 个 11 和 99 个 00。那么我们就枚举出 1414 位二进制的所有可能并加以判断即可,判断思路为判断二进制是否有 99 个 0055 个 11,并且最终酒刚好剩 11 斗。

附上代码

#include <cstdio>
#include <algorithm>

using namespace std;

int main(){

    int value = 0;
    for(int i = 0;i < (1<<14);i ++){
        int total1 = 0;
        int total2 = 0;
        int num = 2;
        for(int j = 0;j < 14;j ++){
            if(i&(1<<j)){//判断j位是否为1
                total1 ++;
                num *= 2;
            }else{
                total2 ++;
                num -= 1;
            }
        }
        if(total1 == 5&&total2 == 9&&num == 1){
            value ++;
        }
    }
    printf("%d\n",value);
    return 0;
}

本文章来自计蒜客。

在C++中,枚举子集有多种实现方法,以下为你详细介绍: ### 二进制法 可以用二进制的一位表示合对应的某一元素的选取状态,1表示选取,0表示未选取。例如,合 {0,1,2,3,4,6} ,二进制 0101101 就代表合 {0,2,3,5}。代码中对于二进制的处理可以用位运算来实现。 ```cpp #include <iostream> using namespace std; int main() { int n; cin >> n; for(int i = 0 ; i < (1 << n) ; i++){ for(int j = n - 1 ; j >= 0 ; j--){ if(i & (1 << j)) cout << 'Y'; else cout << 'N'; } cout << endl; } return 0; } ``` 上述代码中,外层循环遍历所有可能的二进制状态,内层循环检查每一位是否为1,从而确定元素是否被选取 [^1][^5]。 ### 递归法 通过深度优先搜索(DFS)递归地生成所有可能的子集。在每一层递归中,有两种选择:选取当前元素或不选取。 ```cpp #include <iostream> using namespace std; int n; char c[15]; void dfs(int step) { if (step > n) { for (int i = 1; i <= n; i++) { cout << c[i]; } cout << endl; return; } c[step] = 'N'; dfs(step + 1); c[step] = 'Y'; dfs(step + 1); } int main() { cin >> n; dfs(1); return 0; } ``` 上述代码中,`dfs` 函数递归地处理每一个位置,分别尝试选取和不选取当前元素,直到处理完所有位置 [^4]。 ### 从大到小枚举子集法 从大到小枚举 `mask` 的子集,寻找下一个子集。如果 `sub` 为 0,则没有比它小的子集;否则,从右到左寻找第一个不为 0 的位,假定此位是 `i1`,将 `i1` 位减 1,也就是变成 0,`i1` 后面的位取最大,也就是 `mask` 的后 `i1` 位。`mask&(sub - 1)` 就是比 `sub` 小的最大子集。 ```cpp // 此代码仅为逻辑示意,未完整实现输入输出等部分 // 假设已经有了 mask 和 sub // 枚举 mask 的子集 for (int sub = mask; sub; sub = (sub - 1) & mask) { // 处理子集 sub } ``` 通过此方法枚举 `maxMask = (1 << n) - 1` 所有子集子集的时间复杂度是 $O(3^n)$ [^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值