高效判断2的幂次

核心思想

通过二进制特性,判断一个数是否仅有一个二进制位是1。因为2的幂次数的二进制形式满足这一条件(例如:1 (1)2 (10)4 (100)8 (1000) 等)。


关键操作:k & (k - 1)

操作步骤
  1. 计算 k-1
    当 k 的二进制最低位的 1 变为 0,之后所有低位变为 1
    示例

    • k = 8 (1000) → k-1 = 7 (0111)

    • k = 5 (101) → k-1 = 4 (100)

  2. 按位与运算 k & (k-1)
    将 k 和 k-1 的二进制逐位进行与操作。

    • 如果 k 是2的幂次,则结果为 0

    • 如果 k 不是2的幂次,则结果不为 0


数学证明

情况1:k 是2的幂次
  • 二进制形式k 的二进制仅有一个 1(例如 1000)。

  • 计算 k-1:该操作会将 k 的二进制中唯一的 1 变为 0,之后所有低位变为 1(例如 1000 → 0111)。

  • 按位与结果

      1000 (k)
    & 0111 (k-1)
    ----------
      0000

    结果为 0,满足条件。

情况2:k 不是2的幂次
  • 二进制形式k 的二进制中有多个 1(例如 1010)。

  • 计算 k-1:仅影响最低位的 1,其他高位保持不变(例如 1010 → 1001)。

  • 按位与结果

      1010 (k)
    & 1001 (k-1)
    ----------
      1000

    结果不为 0,不满足条件。


边界条件处理

  1. k = 0

    • 0 不是2的幂次,但 0 & (-1) = 0。需额外判断 k > 0

  2. k = 1

    • 1 是2⁰,满足条件。

    • 1 & 0 = 0,结果正确。

代码实现示例

bool isPowerOfTwo(int k) {
    return (k > 0) && ((k & (k - 1)) == 0);
}

优势分析

  1. 时间复杂度O(1),仅需一次减法和按位与操作。

  2. 无依赖:无需特定库函数(如__builtin_popcount),跨平台兼容性强。

  3. 高效性:位运算直接操作内存,速度快于循环或数学函数。

应用 

蓝桥杯入门赛题目-----打花结

问题描述

打花结是一种中国传统手工艺,也被称为“中国结”或“结绳艺术”。它是一种用绳子编织而成的装饰品,常见于节庆、婚礼、生日等重要场合。打花结的形状多样,有圆形、方形、菱形等,每种形状都有其独特的寓意和象征意义。

小蓝最近得到了 NN 张矩形花纸。每张花纸的长为 lili​,宽为 wiwi​。小蓝想要从中选取一些花纸来制作打花结,但只有经过若干次折叠操作后使花纸的面积变为 XX 的花纸才能用于打花结。

折叠操作如下:

  1. 将纸准确对折成一半,形成一条与宽度方向平行的水平对折线,长度减半。

  2. 将纸准确对折成一半,形成一条与长度方向平行的垂直对折线,宽度减半。

请你判断每张花纸是否适合打花结,如果适合,则输出 Yes;否则输出 No

输入格式

第一行输入两个整数 N,X(1≤N≤103,1≤X≤104)N,X(1≤N≤103,1≤X≤104) 表示花纸数量。

接下来 NN 行,每行输入两个整数 li,wi(1≤li,wi≤100)li​,wi​(1≤li​,wi​≤100) 表示花纸的长宽。

输出格式

输出 NN 行,若第 ii 张花纸适合打花结则输出 Yes,否则输出 No

样例输入

3 10
2 5
10 8
3 10

样例输出

Yes
Yes
No

运行限制

语言最大运行时间最大运行内存
C++1s256M
C1s256M
Java2s256M
Python33s256M
PyPy33s256M
Go3s256M
JavaScript3s256M

 代码运用

#include <iostream>
using namespace std;

int main() {
  int n, x;
  cin >> n >> x;
  while (n--) {
    int l, w;
    cin >> l >> w;
    if (x == 0) {
      cout << "No\n";
      continue;
    }
    long long area = (long long)l * w;
   
    if (area % x != 0) {
      cout << "No\n";
      continue;
    }
  
    long long k = area / x;
    
    if (k > 0 && (k & (k - 1)) == 0) {
      cout << "Yes\n";
    } else {
      cout << "No\n";
    }
  }
  return 0;
}

代码解释

代码结构概览

这段代码用于判断多个矩形是否能被分割成指定面积(x)的瓷砖,且瓷砖数量必须是 2的幂次(如1,2,4,8等)。


逐行解析

int main() {
  int n, x;
  cin >> n >> x;
  • 功能

    • 读取测试用例数量 n 和单块瓷砖面积 x

  while (n--) {
    int l, w;
    cin >> l >> w;
  • 功能

    • 循环处理每个矩形,读取其长度 l 和宽度 w


    // 处理 x=0 的情况
    if (x == 0) {
      cout << "No\n";
      continue;
    }
  • 关键点

    • 避免除零错误。若 x=0,无法定义瓷砖面积,直接输出 No


    // 防止整数溢出
    long long area = (long long)l * w;
  • 关键点

    • 将 l 和 w 强制转换为 long long 再相乘,防止乘积溢出(例如 l=1e5w=1e5 时,int 会溢出)。

    // 检查面积是否能被x整除
    if (area % x != 0) {
      cout << "No\n";
      continue;
    }
  • 逻辑

    • 若总面积 area 无法被 x 整除,说明无法用完整瓷砖铺满,直接输出 No


long long k = area / x;

  • 功能

    • 计算需要的瓷砖数量 k

    // 检查k是否为正数且是2的幂次
    if (k > 0 && (k & (k - 1)) == 0) {
      cout << "Yes\n";
    } else {
      cout << "No\n";
    }
  • 核心逻辑

    1. 条件 k > 0

      • 排除 k=0(面积不足)或 k<0(面积或瓷砖面积为负数)的情况。

    2. 位运算 (k & (k-1)) == 0

      • 原理:若 k 是2的幂次,其二进制表示仅有一个 1

        • 例如 k=8 (1000)k-1=7 (0111),按位与结果为 0

      • 意义:高效判断 k 是否为2的幂次,时间复杂度 O(1)


边界条件处理

  1. x=0

    • 直接输出 No,避免除零错误。

  2. 整数溢出

    • 使用 long long 存储面积,防止 l*w 溢出。

  3. k<=0

    • 若 k 非正数(如 x 为负数或面积不足),直接输出 No

代码功能性对比 

#include <iostream>
using namespace std;

int main() {
  int n,x;
  cin>>n>>x;
  while (n--) {
    int l,w;
    cin>>l>>w;
    cout<<(l*w%x == 0 && __builtin_popcount(l*w/x) == 1 ? "Yes" : "No")<<'\n';
  }
  
  return 0;
}

看似简洁实际既不利于理解和使用,而且调用_builtin_popcount库,复杂度大大提升

写者笔记 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值