C++ 运算符
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。
(1) 逻辑运算符
运算符 | 描述 | 实例 |
---|---|---|
&& | 称为逻辑与运算符。如果两个操作数都为非零,则条件为真 | (A&&B)为假 |
|| | 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真 | (A||B)为真 |
! | 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假 | !(A&&B)为真 |
#include <iostream>
using namespace std;
int main()
{
int a = 5;
int b = 20;
int c;
if(a&&b)
cout << "Line 1 - 条件为真" << endl;
if(a||b)
cout << "Line 2 - 条件为真" << endl;
a = 0;
b = 10;
if(a&&b)
cout << "Line 3 - 条件为真" << endl;
else
cout << "Line 4 - 条件不为真" << endl;
if(!(a&&b))
cout << "Line 5 - 条件为真" << endl;
return 0;
}
开灯问题(涉及逻辑运算符问题)
有 n 盏灯,编号为 1~n。第 1 个人把所有灯打开,第2个人按下所有编号为2的倍数的开关(这些灯将被关掉),第3个人按下所有编号为3的倍数的开关(其中关掉的灯将被打开,开着的灯将被关闭),依次类推。一共有k个人,问最后有那些灯开着?输入 n 和 k,输出开着的灯的编号。k<=n<=1000。
样例输入:7 3
样例输出:1 5 6 7
思路:用数组存一下状态。
#include <iostream>
#include <array>
using namespace std;
int main()
{
//有 n 盏灯,k 个人
int n,k;
cin >> n >> k;
array<bool,1010> deng {false};
for(int i=1;i<=k;i++)
{
for(int q=1;q<=n;q++)
{
if(q%i==0){
deng[q] = !deng[q]; //这里的!就是逻辑非,即如果是1则返回0
}
}
}
for(int i=1;i<=n;i++){
if(deng[i]==true)
cout << i << endl;
}
}
(2)位运算符:
位运算符作用于位,并逐位执行操作。&、| 和 ^ 的真值表如下所示:
p | q | p&q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
假设如果 A = 60 ,且 B = 13 ,现在以二进制格式表示,他们如下所示:
A = 0011 1100
B = 0000 1101
A & B = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~A = 1100 0011
下表显示了C++ 支持的位运算符。假设变量 A 值为60,变量 B 的值为13,则:
运算符 | 描述 | 实例 |
---|---|---|
& | 如果同时存在于两个操作数中,二进制AND运算符复制一位到结果中。 | (A&B)将得到12,即为0000 1100 |
| | 如果存在于任一操作数中,二进制OR运算符复制一位到结果中。 | (A|B)将得到61,即为0011 1101 |
^ | 如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中 | (A^B)将得到49,为0011 0001 |
~ | 二进制补码运算符是一元运算符,具有”翻转“位效果,即0变成1,1变成0。 | (~A)将得到-61,即为1100 0011,一个有符号二进制数的补码形式 |
<< | 二进制左移操作符。左操作数的值向左移动右操作数指定的位数 | A<<2 将得到240,即为1111 0000 |
>> | 二进制右移运算符。左操作数的值向右移动右操作数指定的位数 | A>>2 将得到15,即为0000 1111 |
#include <iostream>
using namespace std;
int main()
{
unsigned int a = 60;
unsigned int b = 13;
int c = 0;
c = a & b;
cout << "Line 1 - c 的值是 " << c << endl;
c = a | b;
cout << "Line 2 - c 的值是 " << c << endl;
c = a ^ b;
cout << "Line 3 - c 的值是 " << c << endl;
c = ~a;
cout << "Line 4 - c 的值是 " << c << endl;
c = a << 2;
cout << "Line 5 - c 的值是 " << c << endl;
c = a >> 2;
cout << "Line 6 - c 的值是 " << c << endl;
return 0;
}
说到位运算,首先要知道数字在计算机中的存储方式,这里只说整数。
正整数 在计算机中是以原码的形式存储的。也可以说是补码 或者 反码,因为 正整数的 原码 , 反码 和补码 是一样的。
负整数 在计算机中 是以补码的形式存储的。负数的补码等于负数的反码+1。
原码是什么?
正数的原码就是转化为二进制的这个数,负数的原码就是负数的绝对值转化为二进制,然后在最高位补1。
举例说明:
int类型的 3 的原码
00000000 00000000 00000000 00000011
int类型的 -3 的原码
10000000 00000000 00000000 00000011
反码是什么?
正数的反码就是原码,负数的反码等于原码除符号位以外所有的位取反
举例说明:
int类型的 3 的反码是
00000000 00000000 00000000 00000011
int类型的 -3 的反码是
11111111 11111111 11111111 11111100
补码是什么?
正数的补码与原码相同,负数的补码为反码最低位+1后的值
举例说明:
int类型的 3 的补码是:
00000000 00000000 00000000 00000011
int类型的 -3 的补码是:
11111111 11111111 1111111 11111101
这就解释了,为什么 int a = 1; ~a+1等于-1。对正数取反,则是那个负数的反码再+1,就是补码,又因为负数是补码存储,所以输出是负数值。对负数取反,是正数的小一个的数,由上面可知,再加一就是正数
<< 和 >> 运算符
<<运算符
1 左移1位 10 左移2位 100 (相当于2^n)
n 右移x位 = n/2^x
注意是算术右移,如果是正数最高位补零,如果是负数最高位补1
a^b(快速幂)
求 a 的 b 次方对 p 取模的值。
输入格式
三个整数a,b,p,在同一行用空格隔开。
输出格式
输出一个整数,表示a^b mod p 的值
数据范围
1<=a,b,p<=10^9
输入样例:
3 2 7
输出样例:
2
思路:求a^b%p 如果暴力乘的话,数据范围太大,肯定会爆掉,如果将b用2进制表示
2^0 =
2^1 =
2^2 =
2^4 =
再进行判断如果,b的该二进制位是1的话对最终结果乘上a^b
复杂度是log(n)级别
#include <iostream>
using namespace std;
int main()
{
int a,b,p;
cin >> a >> b >> p;
long long res = 1%p;
while(b)
{
if(b&1)
res = res*1ll*a%p; //目的是把 res * 1ll * a的乘积变成long long类型
a = a *1ll* a%p; //同上
b>>=1;
}
cout << res << endl;
}
64位整数乘法
求 a 乘 b 对 p 取模的值
输入格式
第一行输入整数a,第二行输入整数b,第三行输入整数p。
输出格式
输出一个整数,表示a*b mod p的值。
数据范围
1<=a,b,p<=10^18
输入样例
3
4
5
输出样例
2
思路:仍然是整数溢出问题,如果直接ab就会整数溢出
所以将问题转化为:
a+a+a+…+a
a = a
a = a2
a = a4
a = a8
…
再根据b的位运算
#include <iostream>
using namespace std;
int main()
{
unsigned long long a,b,p;
cin >> a >> b >> p;
unsigned long long res = 0;
while(b)
{
if(b&1) res = (res + a)%p;
a = (a + a)%p;
b>>=1;
}
cout << res << endl;
}