[AcWing]第一篇blog&&位运算学习笔记

传送门

  • 还是开始做blog了
  • 听课笔记
        • 1.补码:
        • 2.ox3f:
        • 3,左移和右移:
        • 4.1ll:
        • 5.异或
        • 6.lowbit
  • 做题打卡
        • 1.ACwing 89 a^b
          • 1.快速幂模板
        • 2.AcWing 90. 64位整数乘法
          • 2.数据各类型范围
          • 3.运算符优先级
        • 3.AcWing 91. 最短Hamilton路径
          • 小技巧

还是开始做blog了

emmmmm,由于大雪菜up在教学视频里的建议,还是打算搞一个blog了,权当笔记本吧,那么,就这样.开始吧

听课笔记

url:www.bilibili.com/video/av40827155

1.补码:

其实就是懒惰的 先辈们想用加法的方式来实现减法效果而引入的一种东西…它的效果是:

x+x的补码=10000…0(0的个数和x的位数相同)

所以x的补码等于~x+1 ,即将x取反加一

2.ox3f:

ox3f是初始化为无穷大的好选择!
如下:
https://blog.csdn.net/xiangyong58/article/details/24927699

1 0x3f3f3f3f的十进制是1061109567,也就是10^9级别的(和0x7fffffff一个数量级),而一般场合下的数据都是小于10 ^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。
2:由于一般的数据都不会大于10^9,所以当我们把无穷大加上一个数据时,它并不会溢出(这就满足了“无穷大加一个有穷的数依然是无穷 大”),事实上0x3f3f3f3f82=2122219134,这非常大但却没有超过32-bit int的表示范围,所以0x3f3f3f3f还满足了我们“无穷大加无穷大还是无穷大”的需求。

3:0x3f3f3f3f还能给我们带来一个意想不到的额外好处:如果我们想要将某个数组清零,我们通常会使用 memset(a,0,sizeof(a))这样的代码来实现(方便而高效),但是当我们想将某个数组全部赋值为无穷大时(例如解决图论问题时邻接矩阵的 初始化),就不能使用memset函数而得自己写循环了我们知道这是因为memset是按字节操作的,它能够对数组清 零是因为0的每个字节都是0,现在好了,如果我们将无穷大设为0x3f3f3f3f,那么奇迹就发生了,0x3f3f3f3f的每个字节都是0x3f!所 以要把一段内存全部置为无穷大,我们只需要memset(a,0x3f,sizeof(a))。

3,左移和右移:

即向左移和向右移,左移n位等 于除以2n,右移n位等于乘以2n
写法:x>>n和x<

4.1ll:

int型转换为long long型的快捷写法:x* 1ll,其他的,1* l和1* 1ll,其他貌似没了,1* 1d、1* lf、1*f等是不合法的写法

5.异或

异或的运算是相同为0,不同为1,也可以;理解为所谓的不进位加法,具体写法是“n^ m”,表示n和m异或的结果。注意:n^ 1等于把n的最后一位由1变0或者相反,例如:11^ 1=10,10^ 1=11,这个东西,图论中存边的时候貌似 经常用到

6.lowbit

树状数组基础,用于求一个数自最低1的位数开始,表示的数,lowbit(n)=(~n+1)&n=(-n)&n,具体为什么这样做,可以模拟一下or看dalao视频。

做题打卡

1.ACwing 89 a^b

题目地址
模板题,要用快速幂

1.快速幂模板

模板(a^b mod p)

int rest=1%p;//为什么要模p呢,考虑一下这种情况:a^0mod p 
while(b)
{
	if(b&1)res=res*a%p;
		//等价于下面这个
		//if(b%2==1)res=res*a%p;
	a=a*a%p;//平方
	b>>2;//指数减半	
}

res即为结果\

2.AcWing 90. 64位整数乘法

题目地址

同样模板题,数据范围扩大至1018,而aB变成了a*b我居然被这种东西给坑到了) 我才没有犯错呢哼!(确信)

做法是类似的,上一题是ab=(a2)(b/2),本题是ab=(2a)*(b/2),记得取余即可,数据类型要用unsigned long long并且取余时加括号

2.数据各类型范围
类型 范围 范围2 字节
char -128 ~ +127 -27~27-1 1
int/long -2147483648 ~ +2147483647 -231~231-1 4
long long -9223372036854775808 ~ +9223372036854775807 -263~263-1 8
unsigned long long 0~18446744073709551615 0~2^64-1 8

其他的感觉不常用?如果觉得有用,就以后添加吧。可以看到ULL比LL在正数范围内大了一倍而字节不变,是因为它把第一位(符号位)拿来存值了。本题没负数而且值很大,所以必须用ULL

3.运算符优先级

本题中,“+”的优先级小于“%”,需要括号,完整的表如下:
(本来打算手打,然而我不会用markdown合并单元格orz,所以就贴图了)

[AcWing]第一篇blog&&位运算学习笔记_第1张图片

完整地址

3.AcWing 91. 最短Hamilton路径

本题涉及到图论的知识,没怎么学图的我表示看完题就有点想放弃

思路:
关注两点:
1:已经走过哪些点?
2:现在在那个点上?

假定经过的点的集合为state_j,停留在的点为 j ,那么:
route [state_j] [j] = route[state_k] [k]+weight[k] [j]

那么现在的问题是:如何用一个数表示一个集合呢?

这个时候就要用到位运算了,题目的最大范围是多少?n=20。而int型有几个二进制位呢?4*8=32>20,所以我们可以用一个int型数来表示集合,它的二进制对应数的第x位为1表示第x个点已经走过了,这样就可以用二维数组来愉快的玩耍啦

如果往集合中添加第j个点,那么就加上1< 贴代码(基本是照着“大雪菜”up的写的orz 我还是把自己的理解写成了注释的)

……
const int N=1<<20 ;

int n,dis[21][21],route[N][21];
//注意route不能开成N*N,会炸,而且只有20个点,根本用不上
int i,j;

int main()
{
	....//输入
	memset(route,0x3f,sizeof(route));
	//初始化为极大值,因为我们接下要用找最小值
	route[1][0]=0;//开始的点为一步都没走
	for(i = 0; i < 1 <<n ; i ++)//枚举已经走过的点的可能集合
	for (j = 0; j < n ; j ++)//枚举所有点
		if(i >> j & 1)//如果第j位是1 ,即包含第j个点
		{
			//退回到第k个点
			for(int k=0;k<n;k++)if(i-(1<<j)>>k&1)
			//剔除退回的第j个点,并判断是否满足第k个点是走到了的 
				route[i][j]=min(route[i][j],route[i-(1<<j)][k]+dis[k][j]);
				//如果第k个点走到j比当前的方式更短 ;更新routei,j;
		} 
	printf("%d",route[(1<<n)-1][n-1]);//结果
	//注意不是route[1<
	return 0;
}

小技巧

本题中学到的一个小技巧:确认一个数的j位是否为1,使用这个:x>>j&1

嘛,第一篇blog,就这么结束了然后位运算的视频还有一多半没看 基本上是《《算法竞赛进阶指南》0.1位运算(上)》的听课笔记和做题,挺乱的 ,但是就这样吧。

你可能感兴趣的:(AcWing听课记录,2019学习笔记)