生成树小总结

无向图最小生成树

Prim算法

这个实现做的非常不好,需要改进。对付稠密图,Prim更胜Kruskal一筹。

Kruskal算法

[这个实现等待填坑]

Borůvka算法

[这个实现等待填坑]

有向图最小生成树(最小树形图)

复杂度O(V * E),以下代码仅存储边,而且会对存储结构发生破坏,请注意。实现来源于国立台湾师范大学。我对这份代码进行了更加详细的注释。

适用于固定了生成树的根的情形

适用于生成树的根无法确定的情形

引用自GentieH,谢谢。

建立虚拟节点x,令x连所有的点,权值为所有边权的总和+1,求得最后结果减去该值即可,由于虚拟节点的存在,不会显式地出现孤立点,这时通过判断如果存在多与1个节点的pre为root,则说明无解。如果需要求出根节点,由于之前将x与图中的点连边时,我们按顺序连的话,考虑到可以用边去标识点,对于每一个终点v,如果它的pre为root,则标记这条边who,在求结果时,让who-E即可。

要求输出方案的情形

CF240E

 

次小生成树

[Pending Session]

增量最小生成树

[Pending Session]

最小瓶颈生成树

[Pending Session]

生成树问题混杂其他类型问题

小技巧实现istream的非空格符分割

只是个小技巧,然而我现在并不能完全理解这个实现的原理,还是很弱的。

大概是自定义locale,重新定义空白符,再将自定义locale导入istream改变istream的行为。无论是cin还是ifstream都是可以用的。注意imbue完locale之后cin的全局特性都会改变,如果想要恢复原来的istream的行为的话要imbue原来的locale.

istream.get()修改delimiter

直接使用get()的第三個參數就可以了,cin.get(目標串, 目標輸入最大長度,分隔符);

但是只能用一個分隔符。

一份仅用于学习的伸展树C实现

这份代码来自于以前的Wikipedia,遇到这份代码也许是缘分吧,虽然实现上十分的冗长(好多好多的废话),但是也正因为如此这份代码展示了伸展树操作的每一个细节,尽管没什么实用价值,但是拿来理解splay是很好的。我自己抄了一遍同时加上了注释。

 

关于欧拉回路和欧拉路径

请留意本文转载自http://www.cppblog.com/abilitytao/archive/2010/07/26/121319.html

如果侵犯了您(原作者)的权益请联系我删除,谢谢。

定义:
欧拉回路:每条边恰好只走一次,并能回到出发点的路径
欧拉路径:经过每一条边一次,但是不要求回到起始点

①首先看欧拉回路存在性的判定:

一、无向图
每个顶点的度数都是偶数,则存在欧拉回路。

二、有向图(所有边都是单向的)
每个节顶点的入度都等于出度,则存在欧拉回路。

三.混合图欧拉回路
混合图欧拉回路用的是网络流。
把该图的无向边随便定向,计算每个点的入度和出度。如果有某个点出入度之差为奇数,那么肯定不存在欧拉回路。因为欧拉回路要求每点入度 = 出度,也就是总度数为偶数,存在奇数度点必不能有欧拉回路。
好了,现在每个点入度和出度之差均为偶数。那么将这个偶数除以2,得x。也就是说,对于每一个点,只要将x条边改变方向(入>出就是变入,出>入就是变出),就能保证出 = 入。如果每个点都是出 = 入,那么很明显,该图就存在欧拉回路。
现在的问题就变成了:我该改变哪些边,可以让每个点出 = 入?构造网络流模型。首先,有向边是不能改变方向的,要之无用,删。一开始不是把无向边定向了吗?定的是什么向,就把网络构建成什么样,边长容量上限1。另新建s和t。对于入 > 出的点u,连接边(u, t)、容量为x,对于出 > 入的点v,连接边(s, v),容量为x(注意对不同的点x不同)。之后,察看是否有满流的分配。有就是能有欧拉回路,没有就是没有。欧拉回路是哪个?查看流值分配,将所有流量非 0(上限是1,流值不是0就是1)的边反向,就能得到每点入度 = 出度的欧拉图。
由于是满流,所以每个入 > 出的点,都有x条边进来,将这些进来的边反向,OK,入 = 出了。对于出 > 入的点亦然。那么,没和s、t连接的点怎么办?和s连接的条件是出 > 入,和t连接的条件是入 > 出,那么这个既没和s也没和t连接的点,自然早在开始就已经满足入 = 出了。那么在网络流过程中,这些点属于“中间点”。我们知道中间点流量不允许有累积的,这样,进去多少就出来多少,反向之后,自然仍保持平衡。
所以,就这样,混合图欧拉回路问题,解了。
②.欧拉路径存在性的判定

一。无向图
一个无向图存在欧拉路径,当且仅当   该图所有顶点的度数为偶数   或者  除了两个度数为奇数外其余的全是偶数

二。有向图
一个有向图存在欧拉路径,当且仅当 该图所有顶点的度数为零     或者 一个顶点的度数为1,另一个度数为-1,其他顶点的度数为0

三。混合图欧拉路径
其实整篇文章只有这部分是我写的哈,灰常不好意思,只是网上的同志们写的太好了,实在没有必要重复劳动,不知道大家有没有发现,求欧拉路径的第一步一定是求欧拉回路,在混合图上也不例外,如何判断混合图欧拉回路问题的存在性呢?首先,我们用上文所说的方法判断该图是否存在欧拉回路,如果存在,欧拉路径一定存在。如果欧拉回路不存在,那么我们枚举欧拉路径的起点和终点,连接一条无向边,然后再用最大流判断是否存在欧拉回路即可。

求有向图强连通分量的Tarjan算法

-_-#上面的ccs应该是scc,名字叫错了跪。。。

关于Tarjan,https://www.byvoid.com/blog/scc-tarjan这个讲的挺好,一定要看看。然后我说下我自己咋想的。

首先初始化,对于每个节点都要分配一个dfn值和low值,开一个tim当作计时器,然后基本的流程就是:

~进入当前节点
~tim++
~当前节点dfn=low=tim
~标记当前结点已经走过(注意图的遍历中节点只会走一次,也就是说标记不用撤销)
~把当前节点压入栈中(这个stack预先开好了)
~开始扫当前节点的分支,如果这个分支走过了,判断一下是不是这个分支是不是在栈里,如果在栈里的话就看看分支的dfn是不是比当前节点的low还小,小的话就把当前的low值调成那个dfn值;如果分支没走过,直接递归进到那个分支里去,等到递归出来之后,如果分支的low值比当前low值小,就令当前low值为分支low值(这步比较难懂)
~等到所有分支扫完,检查一下当前的dfn是不是等于low,如果不相等直接退出当前递归就好;相等的话,说明节点是scc的根,这个时候不断弹出栈,直到当前节点也被弹出,弹出的这些节点放在一起就是一组scc。

其实这个Tarjan的原理就是利用时间标记,进入栈的节点都是预定互相连通的,dfu值的意思就是当遍历到这个节点,是第几步(我把tim当作步来看),low的意思就是从当前节点可以走到的尽量在时间上先走到的栈内节点对应的dfu,就是最优解的对应标号,这个low肯定是越小越好,因为low越小我们的连通回路就越大。如果low=dfu,那就是当前个点已经没有办法和栈里的既有的点连通了,所以当尝试了全部的走法之后还是没有找到一个栈里的节点来和当前点连通,就说明这个连通回路闭合了,这个时候只要把整个连通回路统统从栈里弹出来,就能得到整个连通回路,也就是scc的一个。每步中接下来要走的节点有三种情况:有的节点访问过但是不在栈里,这样的点不用考虑,因为它已经被弹出了,相当于这个节点已经和我们没关系了;有的点访问过而且在栈里,这样的节点有可能就是当前连通回路的终点了,所以我们要比较一下当前low值是不是比它的dfu大,如果大的话说明这个点可以连通过去,如果小的话说明我们已经有一种连通方式可以包括和它连通了,这个不是最优解,我们就忽略好了;有的节点没访问过,这样的点我们要递归进去处理一下,递归完之后这个节点就有了它在当前情形下该有的dfu和low了,因为这个点和当前点连通,所以这个点要是有一个比当前更好的连通方案,我们用它的就好。

基本就是这么搞,这种时间流逝标记的方法很重要(虽然我还不知道到底有多重要。。。)。

 

Linux权限姿势小总结

  • 首先是会读权限:

ls -al的时候,会出来10个字符,可以分为四段:

先说一点,linux下啥都是文件

[d][rwx][rwx][rwx]

第一段是类型,d是目录,-是普通文件,当然还有好多别的(设备啦什么的)
第二段是owner权限,如果没有chown过的话,创建这货的人就是owner,owner的权限看这一段
第三段是group权限,如果没有chgrp或者chown过的话,创建这货的人是哪个组的这个文件的group也就对应哪个组,在这个group内,还不是owner的人,要看这个权限
第四段是other权限如果访问这个文件的人既不是owner,也不是这个文件对应group里面的用户,那就是要看这段权限

  • 然后就是每一段的rwx是啥:

r:read,对于普通文件,就是读取文件内容的权利,对于目录,就是查询目录结构的权利。

w:write,对于普通文件,就是改变文件内容的权利,对于目录,就是改变目录结构的权利(我说过了目录是文件,可以把目录当成一张索引表)。

x:execute,这货就比较特殊,对于普通文件来说,凡是打上x权限的文件,都是可执行文件(这个和windows里面依靠文件类型来区分的机制不一样),对于目录来说,打上x权限的意思是可以进去目录(也就是说即使目录权限是r--,不是r-x,目录也是进不去的,里面的文件不管权限怎么样,也打不开,因为要先进目录再开文件)

  • rwx是三个基本权限,然后还有附加权限三个:

[-][--s][--s][--t]

有时候会像上面那样本来是x的位置变成s或者t了,这个时候说明文件有特别权限

第一个是setuid,这个权限开启与否会显示在owner权限的第3位,如果owner权限有x,显示为s,没有x,显示为S。这个权限的意思是当文件执行时,强行让执行的那个人适用owner的权限(就是说假如这个文件的权限是-rwsr–r–,本来非owner是不能写和执行的,但是因为开了setuid权限,现在谁都可以有写和执行的权利了,因为他们都被当成了文件的owner,用rwx权限)。

第二个是setgid,这个权限只能给目录开,显示在group权限的第三位,有x为s,没x为S。这个setgid的意思就是在这个目录下创建文件的话,不管是谁创建的,新建的文件的group都会被强行设为这个目录的group

第三个是sticky bit,显示在other权限的第三位,有x为t,没x为T。这个权限就是不让删除的意思,防删标记。有一些-rwxrwxrwx的文件比较重要又怕手滑的话可以开这个权限。
该位可以理解为防删除位. 一个文件是否可以被某用户删除, 主要取决于该文件所属的组是否对该用户具有写权限. 如果没有写权限, 则这个目录下的所有文件都不能被删除, 同时也不能添加新的文件. 如果希望用户能够添加文件但同时不能删除文件, 则可以对文件使用sticky bit位. 设置该位后, 就算用户对目录具有写权限, 也不能删除该文件.

  • 到这里关于权限本身的简单的东西基本都有了,然后就是chmod怎么用的问题:

chmod [权限][文件]

这个[权限]的写法有一种比较简单的方式就是数字表示法:

这里把权限分为四组:[特殊][owner][group][other]

对于后三组,如果开r权限,就算4,开w权限,就算2,开x权限,就算1(特殊权限都在第四组开,前三组只负责rwx),其实就是三个二进制位啦,把每一组的权限对应的数字加起来,就是这一组的权限,然后按着顺序把三个组权限都写出来就ok了。
举个例子:rwxr--rw-,翻译完就是7(4+2+1)4(4)6(4+2),chmod 0746 [文件]之后这个文件的权限就是rwxr--rw-了。

第一组如果开setuid,就算4,开setgid就算2,开sticky bit就算1,相加填到第四位上就行。

注意,后三位一定要都给出,第一位没有需要的话,可以不给出,只给三位的话默认就是没有特殊权限.

 

 

Scroll to top