基本概念
与运算 x&y
或运算 x|y
非运算 !x
异或 x^y
补码
1+x=0000…00
1+1111…11=0000…00
2+1111…10=0000…00
x+?=0000…00
?=~x+1(补码)
-n=~n+1(计算机中只有加法用加法来表示减法)
左移<<
7<<2 ==*2^n
111—>11100
右移>>
7>>2 ==/2^n(算数右移,第一位根据符号补充0或1)
111—>1
作用
1,用异或来实现配偶
0,1
2,3
4,5
6,7
0^1=1;
1^1=0;
4^1=5;
5^1=4
成对思想,图论,最小费用流
e[index] e[index^1] 成对
2,lowbit运算
求lowbit(1110010000)=10000
0001101111+1=0001110000
1110010000&0001110000=10000
用来求多少个一的算法复杂度与1有关
int lowbit(n)
{
return (~n+1(-n))&n;
}
位运算的算法复杂度都是O(1)的与电路运算有关
小技巧
mement(f,0x3F,sizeof f)
当为int型数据的时候 0x3f3f3f3f
当计算a+b的时候不会超过0xffffffff
快速幂
例如:3^7
7的二进制位111
3^1=3 (1)
3^2=9 (10)
3^4=81 (100)
3^7=3^1*3^2*3^4=3*9*81
快速幂就是快速算底数的n次幂。其时间复杂度为 O(log₂N), 与朴素的O(N)相比效率有了极大的提高。
核心代码
int qpow(int a,int b int p)
{
int res=1%p;t=a;
while(b)
{
if(b&1)//b&1,不是将n的二进制形式000001按位做与操作,
//只要b的最右边一位是1,为true。可以用来判断是否是偶数
res=res*t%p;
t=t*t%p;
b>>=1;
}
}
ab%p
求 a 的 b 次方对 p 取模的值。
输入格式
三个整数 a,b,p ,在同一行用空格隔开。
输出格式
输出一个整数,表示a^b mod p的值。
数据范围
0≤a,b,p≤109 计算机一秒内能计算大概能计算107左右
数据保证 p≠0
输入样例:
3 2 7
输出样例
2
例如:37
7的二进制位111
31=3 (1)
32=6 (10)
34=12 (100)
37=31+32+34=3612
代码
#include<iostream>
using namespace std;
int main()
{
int a,b,p;
cin>>a>>b>>p;
int res=1%p;
while(b)
{
if(b&1)
res=res*1ll*a%p;//这里的的乘1ll就是将res转换成long long数据类型,也可以直接定义成long long
a=a*1ll*a%p;
b>>=1;
}
cout<<res<<endl;
}
a*b%p
求 a 乘 b 对 p 取模的值。
输入格式
第一行输入整数a,第二行输入整数b,第三行输入整数p。
输出格式
输出一个整数,表示a*b mod p的值。
数据范围
1≤a,b,p≤1018
输入样例:
3
4
5
输出样例:
2
代码
#include<iostream>
using namespace std;
typedef unsigned long long ull;
int main()
{
ull a,b,p;
cin>>a>>b>>p;
ull res=0;
while(b)
{
if(b&1)
res=(res+a)%p;
a=(a+a)%p;
b>>=1;
}
cout<<res<<endl;
return 0;
}
最短Hamilton路径
给定一张 n 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。
(哈密顿路径问题在上世纪七十年代初,终于被证明是“NP完全”的。据说具有这样性质的问题,难于找到一个有效的算法。实际上对于某些顶点数不到100的网络,利用现有最好的算法和计算机也需要比较荒唐的时间(比如几百年)才能确定其是否存在一条这样的路径。从图中的任意一点出发,路途中经过图中每一个结点当且仅当一次,则成为哈密顿回路。)
输入格式
第一行输入整数n。
接下来n行每行n个整数,其中第i行第j个整数表示点i到j的距离(记为a[i,j])。
对于任意的x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]>=a[x,z]。
输出格式
输出一个整数,表示最短Hamilton路径的长度。
数据范围
1≤n≤20
0≤a[i,j]≤107
输入样例:
5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0
输出样例:
18
压缩dp,二进制
1,那些位置已经走过了
2,目前停止在哪个位置
f[state][n]=f[state_k][k]+weight[k][n];
注:state是二进制表达的0,代表没选,1代表选了
例如:选了0,1,3
1101–>11(十进制)
n:指当前位置
state_k是去掉j位的
k去掉j位现在的位置
weight是k位到j位的权重
(后一个,加当前选中的这个,逐渐遍历指导找到最小)
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int nn=20,mm=1<<20;
int f[mm][nn],weight[nn][nn];//c++有两个模块堆和栈,main函数里面定义的都被压在栈,const和全局变量都存在堆里面,这样做可以防止栈爆掉。
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>weight[i][j];
memset(f,0x3f,sizeof f);
f[1][0]=0;
for(int i=0;i<1<<n;i++)
for(int j=0;j<n;j++)
if(i>>j&1) //找到j点在的二进制位置判断
for(int k=0;k<n;k++) //判断前一位怎么加上j位是最小
if(i-(1<<j)>>k&1)
f[i][j]=min(f[i][j],f[i-(1<<j)][k]+weight[k][j]);
cout<<f[(1<<n)-1][n-1]<<endl;
return 0;
}
c++有两个模块堆和栈,main函数里面定义的都被压在栈,const和全局变量都存在堆里面,这样做可以防止栈爆掉。
学习视频点击这里
2020年7月8日16:30:31