Day 4 学习笔记 各种图论

图是什么????

不是我上传的图床上的那些垃圾解释...


一、图:

1.定义

由顶点和边组成的集合叫做图。

2.分类:

边如果是有向边,就是有向图;否则,就是无向图

平常的图一般都有标号,我称之为标号的图(废话)有序图,如果没有标号,就称之为无序图(没标号的图)

注意有向图和无向图转换之后可能不同,然后有序图和无序图转换之后也不同。

3.存储方式

1.基础方式:邻接矩阵

优点:O(1)查询,

缺点:O(n^2)存储



这个图很好的 解释了邻接矩阵的情况。

如果是有向图,可以这样存;无向图就是上下对称的方式记录。

2.链式前向星(模拟链表)

优点:遍历所有出边

缺点:O(1)的询问没了

  1. struct edge{
  2. int from,to,dis,next;
  3. } Edge[100001];
  4. //从哪里来,指向哪里,出边的第一条边,上一条出边的标号
  5. //就像单向链表向头部添加元素一样
  6. //当然,用vector存储也可以

如何添一条边呢?

很明显有两种情况,如果是一开始没有边,就只改first和添的边的next改成0,然后把边的权值改上。如果有边,就塞到最后面

代码实现:

  1. int cnt=1,head[10086];//当前是第几条要加上的边,以及10085条链表的头部
  2. inline void add_edge(int from,int to,int dis)
  3. {
  4. Edge[cnt]=(edge){from,to,dis,head[from]};
  5. head[from]=cnt++; //更新头部
  6. }

二、最短路问题

解决方式十分多(复杂)的问题。

1.Floyd算法(dp)(支持负边权)

  1. for (register int i=1;i<=n;i++)
  2. for (register int j=1;j<=n;j++)
  3. f[i][j]=a[i][j];
  4. for (register int k=1;k<=n;k++)
  5. for (register int i=1;i<=n;i++)
  6. for (register int j=1;j<=n;j++)
  7. f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

令人窒息的O(\(n^3\))和S(\(n^2\)). . . . . .

因为是...简单的求和,所以可以有负边权!(真好)

2.bellman-ford算法

核心:松弛操作。

  1. for (register int i=1;i<=n;i++)
  2. f[i]= INF
  3. for (register int i=1;i<=n;i++)
  4. {
  5. for (register int j=1;j<=m;j++)
  6. {
  7. if (Edge[i].from=i)
  8. {
  9. int to=Edge[i].to;
  10. dis[to]=min(dis[from]+Edge[i].dis,dis[to]);
  11. }
  12. }
  13. }

SPFA 队列优化:

核心:队列方式优化,更新dis值

求初始点到任意一点的值。

就是先开一个队列,然后更新与这个点有关的值,然后出队,然后再次用更新的进去的元素,就更新,然后更新,出队,入队......反复操作......直到队列里没东西了。

队列中就是所谓的白点,而队列外面就是所谓的蓝点。

一个点如果更新了其他的点,那么其他的点就将成为白点,而这个点就将回到蓝点的行列中。被更新的点由于更加优化,所以有可能会再去更新其他的点。

  1. queue<int> qwq;
  2. for (register int i=1;i<=n;i++)
  3. dis[i]=INF;
  4. dis[S]=0; //自己到自己=0
  5. vst[S]=true; //源点将先成白色的
  6. qwq.push(S); //将源点先塞进去
  7. while (!qwq.empty())
  8. {
  9. int now=qwq.front();
  10. qwq.pop();
  11. vst[now]=false; //回归蓝点的行列
  12. for (int i=head[now];i;i=Edge[i].next)
  13. { //遍历以now为起点的边的链
  14. int to=Edge[i].to;//终点
  15. if (dis[to]>dis[now]+Edge[i].dis)//如果距离可以缩减
  16. {
  17. dis[to]=dis[now]+Edge[i].dis;//当然还是缩减了好
  18. if (!vst[to]) //如果还不是白点
  19. {
  20. qwq.push(to);//压进队列
  21. vst[to]=true;//就染色成为白的
  22. }
  23. }
  24. }
  25. }

3.Dijkstra 算法

巨贪无比的算法......(邻接矩阵可用)

基本原理:Dijkstra算法采用的是一种贪心的策略。

首先有一些要开的数组,存一些数据,并且初始化操作。

  1. int dis[maxn]={0},m,n,edge[maxn][maxn];
  2. //声明一个数组dis来保存源点到各个顶点的最短距离
  3. //初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。
  4. //edge[maxn][maxn]邻接矩阵表示边
  5. bool visit[maxn];
  6. //和一个保存已经找到了最短路径的顶点的数组visit。
  7. void input()
  8. {//输入数据
  9. memset(edge,+OO,sizeof(edge));
  10. scanf("%d%d",&n,&m);
  11. int x,y,z;
  12. for (register int i=1;i<=m;i++)
  13. {
  14. scanf("%d%d%d",&x,&y,&z);
  15. edge[x][y]=z;
  16. }
  17. }

Dijstra的方式类似一种dp,采用更新每个点最短路的思想,但是是从一点出发的更新,这是和floyd的不同,这也是Dij只能跑单源最短路问题的根本原因。

因为一开始源点和源点距离是0,知道它的出边,所以把自己到自己更新成+OO,然后把所有出边到达点的距离都更新一个遍,剩下的就全部设置成+OO。

  1. void init(int firstp)
  2. {//初始化
  3. memset(dis,+OO,sizeof(dis));//define +OO 0x7f
  4. dis[firstp]=0;
  5. for (int i=1;i<=n;i++)
  6. if (edge[firstp][i]!=+OO)
  7. dis[i]=edge[firstp][i];
  8. }
  9. /*
  10. 初始时,visit只有源点p是true,
  11. 然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,
  12. 并且把该点加入到T中,此时完成一个顶点,
  13. 然后,我们需要看看新加入的顶点是否可以到达其他顶点,
  14. 并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,
  15. 如果是,那么就替换这些顶点在dis中的值。
  16. 然后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。*/
  17. void dijkstra()
  18. {
  19. while(!q.empty())
  20. {
  21. int lp=q.top().second;
  22. q.pop();
  23. if (visit[lp])continue;
  24. visit[lq]=true;
  25. for (int e=first[lp];e;e=next[e])
  26. if (dis[to[e]]>dis[lp]+value[e])
  27. {
  28. dis[to[e]]=dis[lp]+value[e];
  29. q.push(make_pair(dis[to[e]],to[e]));
  30. }
  31. }
  32. }

三、最小生成树

真好啊,第一次使用最小生成树(逃)

一个有n个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边权

这玩意听着咋这样珂幻?

首先我们经过一些珂幻的证明可以得出——最小生成树性质

设 G=(V,E)是一个连通网络,U是顶点集V的一个非空真子集。若(u,v)是G中一条“一个端点在U中(例如:u∈U),另一个端点不在U中的边(例如:v∈V-U),且(u,v)具有最小权值,则一定存在G的一棵最小生成树包括此边(u,v)。

——摘自百度百科

这个性质有什么用呢?

别急。首先,我用汉语翻译一下刚刚那个百度百科上展示的中国珂学院展示的数学集合语言:首先我们有一个图叫G,然后我们用来存所有点的集合set叫V存所有边的集合叫E(Edge) ,U是V的一个非空真子集,就是不完全包含V的存点的set集合U,这两个东西之间在图内存在一条边一个端点在U,另一个端点不在U这条边就必定是最小生成树的组成部分

还是没啥感觉? 那待会就看下面的LCA部分。

4.Kruskal 算法

怎样实现最小生成树呢?

第一个算法就是Kruskal 算法。

算法的核心思想还是贪心(巨贪无比)。我们已经讲过优先队列(详见Day 3 STL模板库笔记),只需要用优先队列存一些边的集合...

是不是有点思路了?(并没有)

所以,要进行的操作是:

  1. 输入数据
  2. 初始化:\(V_{new}= [x]\)(bool visit[......];visit[x]=true;),其中x为集合V中的任一节点(起始点),\(E_{new}= [Null]\)(优先队列玄学操作),为空;
  3. 反复操作以下几个操作,直到把所有的点都遍历过一遍:

好消息是:一般prim用的是邻接矩阵(太棒了!)

如果还不明白就看一个图例明白一下:



但是当图特别稀疏的时候,prim的优势就显现不出来。这时候我们用到了Kruskal算法。

Kruskal?那是什么?

同样是贪心,这个贪的是边。我们要按照边权从小到大的顺序连边。

我们还是要用到并查集(往下看四、)。

首先我们有这样一个图:



然后连边:

  1. 2->6 1 sum=1
  2. 1->6 1 sum=2
  3. 5->6 1 sum=3
  4. 4->6 2 sum=5
  5. 5->6 2 sum=7

但是注意:如果边的两侧是用一个父亲节点,那么证明连起来是一个环。

所以用到了并查集。并且还要路径压缩版。

然后我们如果遍历完所有点,那就退出来,因为所有的点都在“树”里面了。

上代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <queue>
  5. using namespace std;
  6. const int maxn=2*1e6+1,maxm=5*1e3+1;
  7. struct Edge{
  8. int from,to,value;
  9. friend bool operator < (const Edge &a,const Edge &b)
  10. {
  11. return a.value<b.value;
  12. }
  13. }gragh[maxn];
  14. bool vis[maxm];
  15. int fa[maxm],num_p,num_e,sum,cnt,n,m;
  16. inline int find(int son)
  17. {
  18. if (son==fa[son])return son;
  19. else return fa[son]=find(fa[son]);
  20. }
  21. void kruskal()
  22. {
  23. sort(gragh+1,gragh+m+1);
  24. for (register int i=1;i<=m;i++)
  25. {
  26. int fa_f=find(gragh[i].from);
  27. int fa_t=find(gragh[i].to);
  28. if (fa_f==fa_t){continue;}
  29. sum+=gragh[i].value;
  30. fa[fa_t]=fa_f;
  31. if (++cnt==n-1)break;
  32. }
  33. }
  34. int main()
  35. {
  36. cin>>n>>m;
  37. for (register int i=1;i<=n;i++)
  38. {
  39. fa[i]=i;
  40. }
  41. for (register int i=1;i<=m;i++)
  42. {
  43. cin>>gragh[i].from>>gragh[i].to>>gragh[i].value;
  44. }
  45. kruskal();
  46. cout<<sum;
  47. return 0;
  48. }

5.prim 算法

6.树上倍增算法和LCA(最近公共祖先)

Upd on 2019 7 25:

看到有这么多看这篇文章的

我就更新了最短路的那部分

剩下的最小生成树早晚要更新qwq

Day 4 学习笔记 各种图论的更多相关文章

  1. 图论学习笔记·$Floyd$ $Warshall$

    对于图论--虽然本蒟蒻也才入门--于是有了这篇学习笔记\(qwq\) 一般我们对于最短路的处理,本蒟蒻之前都是通过构建二维数组的方式然后对每两个点进行1次深度或者广度优先搜索,即一共进行\(n\)^2 ...

  2. 图论 竞赛图(tournament)学习笔记

    竞赛图(tournament)学习笔记 现在只是知道几个简单的性质... 竞赛图也叫有向完全图. 其实就是无向完全图的边有了方向. ​ 有一个很有趣的性质就是:一个tournament要么没有环,如果 ...

  3. OI知识点|NOIP考点|省选考点|教程与学习笔记合集

    点亮技能树行动-- 本篇blog按照分类将网上写的OI知识点归纳了一下,然后会附上蒟蒻我的学习笔记或者是我认为写的不错的专题博客qwqwqwq(好吧,其实已经咕咕咕了...) 基础算法 贪心 枚举 分 ...

  4. 广度优先搜索 BFS 学习笔记

    广度优先搜索 BFS 学习笔记 引入 广搜是图论中的基础算法之一,属于一种盲目搜寻方法. 广搜需要使用队列来实现,分以下几步: 将起点插入队尾: 取队首 \(u\),如果 $u\to v $ 有一条路 ...

  5. 深度优先搜索 DFS 学习笔记

    深度优先搜索 学习笔记 引入 深度优先搜索 DFS 是图论中最基础,最重要的算法之一.DFS 是一种盲目搜寻法,也就是在每个点 \(u\) 上,任选一条边 DFS,直到回溯到 \(u\) 时才选择别的 ...

  6. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  7. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  8. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  9. 2014年暑假c#学习笔记目录

    2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...

随机推荐

  1. 《SLAM十四讲》个人学习知识点梳理

    0.引言 从六月末到八月初大概一个月时间一直在啃SLAM十四讲[1]这本书,这本书把SLAM中涉及的基本知识点都涵盖了,所以在这里做一个复习,对这本书自己学到的东西做一个梳理. 书本地址:http:/ ...

  2. 沧桑巨变中焕发青春活力-记极1s HC5661A 打怪升级之路

    最近发现一个新货umaxhosting年付10美元的便宜VPS.2杯喜茶的价格可以让你在国外拥有一个1024MB (1GB) DDR3 RAM.1024MB (1GB) vSwap.70GB RAID ...

  3. cadence allegro16.6 pcb文件转pads pcb文件方法教程

    在pcb设计工作中,有时会被要求将pcb文件转成其他软件的格式,pcb Allegro装Pads的方法如下. 在转换的过程中我们需要用到三种软件,ad.pads.allegro.转换的流程是:alle ...

  4. gitlab 配置 ssh key

    打开本地git bash,使用如下命令生成ssh公钥和私钥对 ssh-keygen -t rsa -C 'xxx@xxx.com' 然后一路回车(-C 参数是你的邮箱地址) 然后打开~/.ssh/id ...

  5. 在Visual Studio中使用.lib和.dll的环境搭建

    1 静态库和动态链接库的区别 动态链接库是在运行的时候被调用的,静态库在链接的时候被链接到最终生成的应用程序(.exe)中 静态库需要用到的文件 (.lib .h) 头文件(.h)提供接口,库文件(. ...

  6. 增强学习训练AI玩游戏

    1.游戏简介 符号A为 AI Agent. 符号@为金币,AI Agent需要尽可能的接取. 符号* 为炸弹,AI Agent需要尽可能的躲避. 游戏下方一组数字含义如下: Bomb hit: 代表目 ...

  7. 如何防范和应对Redis勒索,腾讯云教你出招

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯云数据库 TencentDB发表于云+社区专栏 9月10日下午,又一起规模化利用Redis未授权访问漏洞攻击数据库的事件发生,此次 ...

  8. 从零开始的Python学习Episode 21——socket基础

    socket基础 网络通信要素: A:IP地址   (1) 用来标识网络上一台独立的主机 (2) IP地址 = 网络地址 + 主机地址(网络号:用于识别主机所在的网络/网段.主机号:用于识别该网络中的 ...

  9. windows docker 安装cloudera/quickstart

    最近需要写一个大数据的项目,但是公司没有测试环境,真是cao蛋,没办法,只能自己搭建一个测试环境,所以就在本地电脑装一个cloudera/quickstart,这个是一个单节点的大数据平台, 是clo ...

  10. python清空列表的方法

    1.大数据量的list,要进行局部元素删除,尽量避免用del随机删除,非常影响性能,如果删除量很大,不如直接新建list,然后用下面的方法释放清空旧list. 2.对于一般性数据量超大的list,快速 ...