核心思想
通过二进制特性,判断一个数是否仅有一个二进制位是1。因为2的幂次数的二进制形式满足这一条件(例如:1 (1), 2 (10), 4 (100), 8 (1000) 等)。
关键操作:k & (k - 1)
操作步骤
-
计算
k-1:
当k的二进制最低位的1变为0,之后所有低位变为1。
示例:-
k = 8 (1000)→k-1 = 7 (0111) -
k = 5 (101)→k-1 = 4 (100)
-
-
按位与运算
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,不满足条件。
边界条件处理
-
k = 0:-
0不是2的幂次,但0 & (-1) = 0。需额外判断k > 0。
-
-
k = 1:-
1是2⁰,满足条件。 -
1 & 0 = 0,结果正确。
-
代码实现示例
bool isPowerOfTwo(int k) {
return (k > 0) && ((k & (k - 1)) == 0);
}
优势分析
-
时间复杂度:
O(1),仅需一次减法和按位与操作。 -
无依赖:无需特定库函数(如
__builtin_popcount),跨平台兼容性强。 -
高效性:位运算直接操作内存,速度快于循环或数学函数。
应用
蓝桥杯入门赛题目-----打花结
问题描述
打花结是一种中国传统手工艺,也被称为“中国结”或“结绳艺术”。它是一种用绳子编织而成的装饰品,常见于节庆、婚礼、生日等重要场合。打花结的形状多样,有圆形、方形、菱形等,每种形状都有其独特的寓意和象征意义。
小蓝最近得到了 NN 张矩形花纸。每张花纸的长为 lili,宽为 wiwi。小蓝想要从中选取一些花纸来制作打花结,但只有经过若干次折叠操作后使花纸的面积变为 XX 的花纸才能用于打花结。
折叠操作如下:
-
将纸准确对折成一半,形成一条与宽度方向平行的水平对折线,长度减半。
-
将纸准确对折成一半,形成一条与长度方向平行的垂直对折线,宽度减半。
请你判断每张花纸是否适合打花结,如果适合,则输出 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++ | 1s | 256M |
| C | 1s | 256M |
| Java | 2s | 256M |
| Python3 | 3s | 256M |
| PyPy3 | 3s | 256M |
| Go | 3s | 256M |
| JavaScript | 3s | 256M |
代码运用
#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=1e5,w=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";
}
-
核心逻辑:
-
条件
k > 0:-
排除
k=0(面积不足)或k<0(面积或瓷砖面积为负数)的情况。
-
-
位运算
(k & (k-1)) == 0:-
原理:若
k是2的幂次,其二进制表示仅有一个1。-
例如
k=8 (1000),k-1=7 (0111),按位与结果为0。
-
-
意义:高效判断
k是否为2的幂次,时间复杂度O(1)。
-
-
边界条件处理
-
x=0:-
直接输出
No,避免除零错误。
-
-
整数溢出:
-
使用
long long存储面积,防止l*w溢出。
-
-
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库,复杂度大大提升
写者笔记


8054

被折叠的 条评论
为什么被折叠?



