zoukankan      html  css  js  c++  java
  • 基础重修笔记

    2020.11.16

    今天学的东西比较多,由于是按照紫书上的顺序来学的,就稍微整理一下。

    • 栈与队列

    • 链表

    • 树和二叉树

    栈和队列

    关于队列的例题里就一道,关于双向队列,而且题意实在是看不懂,被迫放弃了。(普通队列和优先队列我应该还熟吧)

    关于栈的题做了两道,感觉运用能力提高了一些。栈这东西貌似在对付括号这方面有奇效,有时间要去做一下括号树了。栈的定义很简单,就是一个LIFO表,即Last in First out,后进先出的意思。主要得学会应用。

    一道例题:UVA442 矩阵链乘

    链表

    感觉上学的并不是很懂,不过有点似懂非懂的意味?其实大概也就是用一个特殊的数组把所有数组内的东西串联起来,可以更方便移动数组内的元素。不过说实话,链式前向星应当也是一种链表,感觉就是这样。

    具体的东西还是要看代码实现。嘴上说说作用似乎不大,所以就放一道双向链表的经典例题在这吧:UVA12657 移动盒子

    至于应用,其实我也说了,一般只有要求要大量移动数组内的元素的题才用得上链表,至少我做的两道例题都是如此,也许明天做的习题会有新花样?

    树与二叉树

    1.二叉树的编号

    嗯,总之记住一个基本的东西,就是左子树编号=根的编号×2,右子树编号=根的编号×2+1。单考编号的话应该考不到很难,只需熟练地掌握基本性质即可。当然,一定的分析能力必不可少,这就需要花时间去提升了。

    2.二叉树的层次遍历

    一堆指针,直接被劝退,等我哪一天学好了指针再说吧……

    3.二叉树的递归遍历

    二叉树的3种遍历方式:先序遍历,中序遍历以及后序遍历。之前看白书的时候怎么看都看不懂,现在用递归版的定义就清晰明了的多了。

    Preorder(T)=T的根节点+Preorder(T的左子树)+Preorder(T的右子树)

    Inorder(T)=Inorder(T的左子树)+T的根节点+Inorder(T的右子树)

    Postorder(T)=Postorder(T的左子树)+Postorder(T的右子树)+T的根节点

    本小节有三道例题,都是对递归非常好的应用,应当掌握。

    4.非二叉树

    平时会碰到的题大部分都是二叉树的,非二叉树实在太少,就随意讲了。非二叉树的题给你的树一般也不是随便的,可能也就是三叉树,四叉树那样的,能熟练掌握递归建树就行了,大概就这一个要点。

    1.用DFS求联通块

    感觉跟求强连通分量差不多,只要对于每一个特殊地形,都用一次深度优先遍历将整张图都跑一遍,同时注意去重就行了。

    2.用BFS求最短路

    这个最简单的用法就不说了,我早就会了。还是看看更难的例题吧:UVA816 Abbott的复仇

    实际题目中要求会更多,比如朝向之类的状态可能会出现,但其实也不用太担心,对于每一种状态都分别处理好就行了,只是有时可能真的会麻烦一点。

    3.拓扑排序

    例题太水了不说。当时学缩点的时候就顺便学了,笔记在图论那一块有,翻翻就是了。

    4.欧拉回路

    看了看,感觉好像也没什么,没有想象中难。

    有欧拉回路或欧拉道路的一个先决条件是图联通,这个DFS一遍即可判断。其次要记录每个点的入度与出度。

    由于欧拉回路与欧拉道路的特殊性,它要求最多只能有两个奇点,即度数为奇数的点。这两个奇点分别是欧拉道路的起点与终点。

    起点的出度比入度大1,终点的入度比出度大1。如果没有奇点,就可以从任何点出发,最终都可以回到该点,即为欧拉回路。


    2020.11.17

    今天没有学新的知识,只是在做昨天学的东西的习题。感觉思维还有待提升,不看题解大概也就做一些比较裸的题,数据结构还是比较难的。尤其是书上的习题,感觉都很难。

    主要是UVa全是字符串,实在过于难受,输入输出都搞得很麻烦,而且UVa天天多组数据还不告诉你数据组数,属实可恶,至于卡格式更是UVa特色。

    做了几道简单一点的习题,收获比较大的大概是求先后序排列?不过这东西可能也就初赛会考考,复赛应该不会考这么冷门的东西,紫书有些东西实在是过时了。

    找了以前的弱省省选题做了一些,做的都是很简单的,一遍过,发现以前的省选里简单题还确实是简单的。虽然真的是很久以前的省选题了

    明天开始学新知识了,希望书上的暴力求解法对比赛真的有用吧。

    最后感叹,生活在一个没有字符串的国度实在是太好了。该死的UVa就不能多用用阿拉伯数字吗

    2020.11.18

    开始学习暴力求解专题,学了很多新东西,其实并没有。跟着书上做了几道简单枚举题,发现有时候无脑枚举也不可行,稍微思考一下可以大大减少枚举量。

    学会了枚举全排列,生成子集,感觉以后可能会用到,位运算的方法比较新颖独特,代码放在这里。

    void print_subset(int n,int s)
    {
    	for(int i=0;i<n;i++)
    	 if(s&(1<<i))printf("%d ",i);
    	printf("
    ");
    }
    int main()
    {
    	for(int i=0;i<(1<<n);i++)
    	print_subset(n,i);
    }
    

    利用此代码可以生成0~n-1的所有子序列。

    枚举排列只要会用这个STL就行了

    next_permutation(p,p+n);
    

    可以直接得到p的下一个排列,就是不知道考场上能不能用。听说带下划线的库函数都不能用?

    顺带重修了dfs。所以说,暴力求解法要是没有回溯法就不叫暴力了。果然dfs是暴力的归宿啊。

    做了做书上的例题,有的还需要剪枝,或者跟树混在一起,所以说,树这东西真的不想碰到,一碰到问题就会变得麻烦起来。

    不过树也就那样,毕竟只有左子树右子树,每次分类讨论还是挺清晰的。

    还重修了bfs,发现这和原来提高篇的bfs小节有点像,感觉高级的一些bfs也就是把状态弄的复杂了一点,之前二维的xy给你弄成八维九维的,当然不可能让你爆空间,所以每个维度的可选物都很少,但是指数级别就完全开不下,而且浪费很多。

    典型例题:P1379 八数码难题

    会将状态进行压缩就行了,将一个数组压成一个数字,便于去重。压缩方式的话,使用哈希函数是最好的了,当然还要解决冲突,这题也可以用set去重,但是用STL虽然方便,效率却差的多,亲测慢了近3s,学会用哈希判重还是很有必要的。

    提高篇上的例题是这道:P2730 [USACO3.2]魔板

    其实完全没有区别,就是操作不一样罢了,状态压缩是完全一致的。对付这种枚举排列的题有一个完美哈希,即不会产生冲突的哈希,那就是使用康托展开的技巧,详情可见模板题:P5367 【模板】康托展开

    事实证明提高篇比起紫书还是差远了,上来就一个康托展开,标题写着Hash判重但是完全没有提及,结果我没头没脑的学了个康托展开就过了,相比之下紫书给了三种不同的解法,分别用康托,哈希表和set,讲的很清楚,孰优孰劣一目了然。

    写了会例题后就去找了点水题做做,感觉花样还是很多的。比如发现了一个有三维状态的题,数据范围小,不用压缩,但是写的比较累。

    然后还尝试了多个点同时拓展,感觉比较新颖,题目放这:P1332 血色先锋队。思路其实也比较简单,其实只要用vector一口气把所有队列中的结点都存下来,然后再一个个循环拓展就行了,完全不会有问题。

    顺带水了一道dfs求联通块的题,复习了一下新知识。

    今日吐槽

    主题库评测就是快,不像提交UVa的题库,问题天天有,运气好可能几秒,运气差几分钟,运气再差可能就永远waiting了。更别提卡格式,多组数据,字符串处理等等毛病。多一个空行就WA,我也是醉了。

    补充:

    今天被wzy安利去做CF的题,感觉ABC都是很简单的题,但是有时那一下弯就很难转过来,也许下个星期会去找点CF的简单题来做,应该可以锻炼到我的思维。

    还是想说:就算是CF速度也比UVa快多了,都和主题库速度一样了!都是外国的网站差距也太大了吧!

    希望能早日脱离UVa的苦海啊。

    2020.11.19

    差点忘记还要写博客了。今天又是刷水题的一天。

    开始找UVa恶心自己前先水了几道CF的A题,感觉自己可以了。然后看着题解把一道状态压缩bfs打过去了,格式日常被卡好几回,居然还要输入换行符才能过得了,见识到了。

    scanf("%d%d
    ",&n,&m);
    

    不加 居然会RE,这是什么神仙错误,记下来记下来。

    开始学习迭代加深搜索,感觉还是比较简单,和估价函数配合起来就会变成IDA*算法,感觉都不算难。但是估价函数有时候没那么好推。(而且要处理题目那千奇百怪的状态真的好难啊)

    做了一道被恶评的裸IDA,传送门在此:UVA1374 快速幂计算。还有双倍经验,但是是绿的。然后去找了点IDA的题,都是蓝的,但是都好难啊。果然是恶评

    明天还要多做点IDA*的题,但是真的想早点脱离搜索的苦海了。状态压缩真的好烦啊。

    虽说如此搜索仍然不得不学,而且还得好好学。所以就放一个迭代加深的板子在这,当然仅供参考。

    int IDA(int d)
    {
    	if(d+h(x)>maxd)return 0;
    	if(d==n)return 1;//(满足条件)
    	for(int i=1;i<=n;i++)
    	{
    		if((IDA(d+1)))return 1;
    	}
    	return 0;
    }
    int main()
    {
    	//do something
    	for(maxd=0;;maxd++)if(IDA(0))break;
    	cout<<maxd;
    }
    

    真的仅供参考,尤其是函数部分,真要应用起来哪有这么简单……也就主函数里的比较模板一点了。

    然后晚上不想做IDA*了,就去刷搜索水题,发现自己居然连绿题也刷不动了。居然要dfs+dp,你不是搜索题

    看题解发现还有一道弱化版的,不用dfs枚举的dp题,发现是道背包,于是就A掉了,还顺带发现并做掉了一道类似的NOIP2018day1T2。

    绕了一圈回过头来做这道最开始的搜索题,dp部分很愉快的就打过去了,结果没过样例,调调调发现没清空dp数组,无语。

    改了之后交一发,TLE了,遂改了一下枚举方式,结果只多过了一个点。无奈之下学习题解,改了一下发现A掉了,快了非常多。

    方法值得学习,所以代码就放在这里。

    以前我都是这么写的

    for(int i=1;i<=n;i++)
    {
        if(!ex[i])
        {
    		ex[i]=1;
    		dfs(now+1);
    		ex[i]=0;
        }
    }
    

    TLE后改成这样了。

    for(int i=last+1;i<=n;i++)
    {
    	ex[i]=1;
    	last=i;
    	dfs(now+1);
    	ex[i]=0;
    }
    

    结果A了。

    我左思右想也没想通,我不就让程序多判断了几次,而且 (nle20) ,这能多出多少枚举量来?

    事实证明我错的离谱,这么小的数据范围也能增这么多枚举量,看来以后要改变习惯。

    不要忽视任何一个优化,因为你不知道它将给你减少多少万次循环。--沃兹基·朔德。

    今日总结:思维依旧严重不足,感觉一天天的大脑都快要生锈了,大概也就是能水一水普及-的题?没学过可能还不一定能过得了

    2020.11.20

    今天划水现象严重,在此严厉批评。

    上午使用A*算法做了两道k短路,模板是黑的,所以今天A了第一道黑题(可喜可贺)。近11点写完两道题就去逛水区了,结果剩余时间只水了3道CF简单题完成任务,应当反省。

    用A*解k短路问题思路如下。

    首先要知道k短路如何求,我们知道一般的优先队列Dij求最短路,第一次遇到终点时就是最短路,那么其实当我们第k次将终点入队时,得到的就是到终点的第k短路径。

    但是我们直接去求是一定会超时的,这时候就要用到A*算法的估价函数来减少循环次数,优先级的判断需要转换,我们需要先求出终点到其他所有点的最短路径,用数组f存储,然后优先级处理时用当前距离dis加上预估距离f来判断。

    因为f已经是从此点到终点的最短路径,所以估价函数设计正确,实际长度不会比估计长度更小。

    注意:估价函数剪枝效果非常不稳定,虽然大多数时候不会TLE,但是会因为队列内存储状态过多而MLE。简而言之,A*算法和SPFA一样都会被卡,但是卡的力度并不很大,在洛谷的题中都只有一个点来卡,一般可以获得90分,必须打表才可过。

    代码就不放了,可以到模板题去看。

    下午做题没有方向,划水划了一个小时,不想写IDA*,又不知道学什么好。于是就稍微看了看蓝书和黑书中关于约数的数学知识,并做了几道题。

    稍微整理一些基础数学知识。

    算术基本定理

    任意一个大于 (1) 的正整数都能唯一分解为有限个质数的乘积,可写作:

    [N=p_1^{c_1}p_2^{c_2}dots p_m^{c_m} ]

    其中 (c_i) 都是正整数, (p_i) 都是质数,且满足 $p_1<p_2<dots<p_m $。

    试除法分解质因数代码实现

    void divide
    {
    	m=0;
    	for(int i=2;i*i<=n;i++)
    	{
    		if(n%i==0)
    		{
    			p[++m]=i,c[m]=0;
    			while(n%i==0)n/=i,c[m]++;
    		}
    	}
    	if(n>1)p[++m]=n,c[m]=1;
    	for(int i=1;i<=m;i++)
    	cout<<p[i]<<'^'<<c[i]<<endl;
    }
    

    由算术基本定理可推得,N的正约数集合为:

    [{p_1^{b_1}p_2^{b_2}dots p_m^{b_m}}, ext{其中}0le b_ile c_i ]

    N的正约数个数为

    [(c_1+1)*(c_2+1)*ldots *(c_m+1)=prodlimits_{i=1}^m(c_i+1) ]

    N的所有正约数的和为

    [(1+p_1+p_1^2+ldots +p_1^{c_1}*ldots *(1+p_m+p_m^2+ldots +p_m^{c_m})=prodlimits_{i=1}^m(sumlimits_{j=0}^{c_i}(p_i)^j) ]

    求正约数个数的公式用的比较多。

    例题:反素数

    晚上还划了很久的水,需要好好反省。就没做什么题了,做了道位运算。

    反省结果就是没有明确目标,于是剩余时间就一直在搞之后的学习计划与任务清单,好在终于搞完了。

    总之,紫书的部分已经完结了,马上要告别UVa好耶,投入到更多的题目中去了。

    完结,明天会开新的一章。

  • 相关阅读:
    python 数据类型 基础第二天
    Python基础第一篇
    前言、入门程序、常量、变量
    win10打开移动热点让手机连接上网教程
    win10移动热点问题
    博客园快速美化
    Idea提示没有符号类错误解决
    mybatis复习01
    test
    d190305面试题01总结
  • 原文地址:https://www.cnblogs.com/luotao0034/p/14063192.html
Copyright © 2011-2022 走看看