Day 4 学习笔记 各种图论
Day 4 学习笔记 各种图论
图是什么????
不是我上传的图床上的那些垃圾解释...
一、图:
1.定义
由顶点和边组成的集合叫做图。
2.分类:
边如果是有向边,就是有向图;否则,就是无向图。
平常的图一般都有标号,我称之为标号的图(废话)有序图,如果没有标号,就称之为无序图(没标号的图)
注意有向图和无向图转换之后可能不同,然后有序图和无序图转换之后也不同。
3.存储方式
1.基础方式:邻接矩阵
优点:O(1)查询,
缺点:O(n^2)存储
这个图很好的 解释了邻接矩阵的情况。
如果是有向图,可以这样存;无向图就是上下对称的方式记录。
2.链式前向星(模拟链表)
优点:遍历所有出边
缺点:O(1)的询问没了
struct edge{
int from,to,dis,next;
} Edge[100001];
//从哪里来,指向哪里,出边的第一条边,上一条出边的标号
//就像单向链表向头部添加元素一样
//当然,用vector存储也可以
如何添一条边呢?
很明显有两种情况,如果是一开始没有边,就只改first和添的边的next改成0,然后把边的权值改上。如果有边,就塞到最后面。
代码实现:
int cnt=1,head[10086];//当前是第几条要加上的边,以及10085条链表的头部
inline void add_edge(int from,int to,int dis)
{
Edge[cnt]=(edge){from,to,dis,head[from]};
head[from]=cnt++; //更新头部
}
二、最短路问题
解决方式十分多(复杂)的问题。
1.Floyd算法(dp)(支持负边权)
for (register int i=1;i<=n;i++)
for (register int j=1;j<=n;j++)
f[i][j]=a[i][j];
for (register int k=1;k<=n;k++)
for (register int i=1;i<=n;i++)
for (register int j=1;j<=n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
令人窒息的O(\(n^3\))和S(\(n^2\)). . . . . .
因为是...简单的求和,所以可以有负边权!(真好)
2.bellman-ford算法
核心:松弛操作。
for (register int i=1;i<=n;i++)
f[i]= INF
for (register int i=1;i<=n;i++)
{
for (register int j=1;j<=m;j++)
{
if (Edge[i].from=i)
{
int to=Edge[i].to;
dis[to]=min(dis[from]+Edge[i].dis,dis[to]);
}
}
}
SPFA 队列优化:
核心:队列方式优化,更新dis值
求初始点到任意一点的值。
就是先开一个队列,然后更新与这个点有关的值,然后出队,然后再次用更新的进去的元素,就更新,然后更新,出队,入队......反复操作......直到队列里没东西了。
队列中就是所谓的白点,而队列外面就是所谓的蓝点。
一个点如果更新了其他的点,那么其他的点就将成为白点,而这个点就将回到蓝点的行列中。被更新的点由于更加优化,所以有可能会再去更新其他的点。
queue<int> qwq;
for (register int i=1;i<=n;i++)
dis[i]=INF;
dis[S]=0; //自己到自己=0
vst[S]=true; //源点将先成白色的
qwq.push(S); //将源点先塞进去
while (!qwq.empty())
{
int now=qwq.front();
qwq.pop();
vst[now]=false; //回归蓝点的行列
for (int i=head[now];i;i=Edge[i].next)
{ //遍历以now为起点的边的链
int to=Edge[i].to;//终点
if (dis[to]>dis[now]+Edge[i].dis)//如果距离可以缩减
{
dis[to]=dis[now]+Edge[i].dis;//当然还是缩减了好
if (!vst[to]) //如果还不是白点
{
qwq.push(to);//压进队列
vst[to]=true;//就染色成为白的
}
}
}
}
3.Dijkstra 算法
巨贪无比的算法......(邻接矩阵可用)
基本原理:Dijkstra算法采用的是一种贪心的策略。
首先有一些要开的数组,存一些数据,并且初始化操作。
int dis[maxn]={0},m,n,edge[maxn][maxn];
//声明一个数组dis来保存源点到各个顶点的最短距离
//初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。
//edge[maxn][maxn]邻接矩阵表示边
bool visit[maxn];
//和一个保存已经找到了最短路径的顶点的数组visit。
void input()
{//输入数据
memset(edge,+OO,sizeof(edge));
scanf("%d%d",&n,&m);
int x,y,z;
for (register int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
edge[x][y]=z;
}
}
Dijstra的方式类似一种dp,采用更新每个点最短路的思想,但是是从一点出发的更新,这是和floyd的不同,这也是Dij只能跑单源最短路问题的根本原因。
因为一开始源点和源点距离是0,知道它的出边,所以把自己到自己更新成+OO,然后把所有出边到达点的距离都更新一个遍,剩下的就全部设置成+OO。
void init(int firstp)
{//初始化
memset(dis,+OO,sizeof(dis));//define +OO 0x7f
dis[firstp]=0;
for (int i=1;i<=n;i++)
if (edge[firstp][i]!=+OO)
dis[i]=edge[firstp][i];
}
/*
初始时,visit只有源点p是true,
然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,
并且把该点加入到T中,此时完成一个顶点,
然后,我们需要看看新加入的顶点是否可以到达其他顶点,
并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,
如果是,那么就替换这些顶点在dis中的值。
然后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。*/
void dijkstra()
{
while(!q.empty())
{
int lp=q.top().second;
q.pop();
if (visit[lp])continue;
visit[lq]=true;
for (int e=first[lp];e;e=next[e])
if (dis[to[e]]>dis[lp]+value[e])
{
dis[to[e]]=dis[lp]+value[e];
q.push(make_pair(dis[to[e]],to[e]));
}
}
}
三、最小生成树
真好啊,第一次使用最小生成树(逃)
一个有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模板库笔记),只需要用优先队列存一些边的集合...
是不是有点思路了?(并没有)
所以,要进行的操作是:
- 输入数据
- 初始化:\(V_{new}= [x]\)(
bool visit[......];visit[x]=true;
),其中x为集合V中的任一节点(起始点),\(E_{new}= [Null]\)(优先队列玄学操作
),为空; - 反复操作以下几个操作,直到把所有的点都遍历过一遍:
好消息是:一般prim用的是邻接矩阵(太棒了!)
如果还不明白就看一个图例明白一下:
但是当图特别稀疏的时候,prim的优势就显现不出来。这时候我们用到了Kruskal算法。
Kruskal?那是什么?
同样是贪心,这个贪的是边。我们要按照边权从小到大的顺序连边。
我们还是要用到并查集(往下看四、)。
首先我们有这样一个图:
然后连边:
2->6 1 sum=1
1->6 1 sum=2
5->6 1 sum=3
4->6 2 sum=5
5->6 2 sum=7
但是注意:如果边的两侧是用一个父亲节点,那么证明连起来是一个环。
所以用到了并查集。并且还要路径压缩版。
然后我们如果遍历完所有点,那就退出来,因为所有的点都在“树”里面了。
上代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=2*1e6+1,maxm=5*1e3+1;
struct Edge{
int from,to,value;
friend bool operator < (const Edge &a,const Edge &b)
{
return a.value<b.value;
}
}gragh[maxn];
bool vis[maxm];
int fa[maxm],num_p,num_e,sum,cnt,n,m;
inline int find(int son)
{
if (son==fa[son])return son;
else return fa[son]=find(fa[son]);
}
void kruskal()
{
sort(gragh+1,gragh+m+1);
for (register int i=1;i<=m;i++)
{
int fa_f=find(gragh[i].from);
int fa_t=find(gragh[i].to);
if (fa_f==fa_t){continue;}
sum+=gragh[i].value;
fa[fa_t]=fa_f;
if (++cnt==n-1)break;
}
}
int main()
{
cin>>n>>m;
for (register int i=1;i<=n;i++)
{
fa[i]=i;
}
for (register int i=1;i<=m;i++)
{
cin>>gragh[i].from>>gragh[i].to>>gragh[i].value;
}
kruskal();
cout<<sum;
return 0;
}
5.prim 算法
6.树上倍增算法和LCA(最近公共祖先)
Upd on 2019 7 25:
看到有这么多看这篇文章的
我就更新了最短路的那部分
剩下的最小生成树早晚要更新qwq
Day 4 学习笔记 各种图论的更多相关文章
- 图论学习笔记·$Floyd$ $Warshall$
对于图论--虽然本蒟蒻也才入门--于是有了这篇学习笔记\(qwq\) 一般我们对于最短路的处理,本蒟蒻之前都是通过构建二维数组的方式然后对每两个点进行1次深度或者广度优先搜索,即一共进行\(n\)^2 ...
- 图论 竞赛图(tournament)学习笔记
竞赛图(tournament)学习笔记 现在只是知道几个简单的性质... 竞赛图也叫有向完全图. 其实就是无向完全图的边有了方向. 有一个很有趣的性质就是:一个tournament要么没有环,如果 ...
- OI知识点|NOIP考点|省选考点|教程与学习笔记合集
点亮技能树行动-- 本篇blog按照分类将网上写的OI知识点归纳了一下,然后会附上蒟蒻我的学习笔记或者是我认为写的不错的专题博客qwqwqwq(好吧,其实已经咕咕咕了...) 基础算法 贪心 枚举 分 ...
- 广度优先搜索 BFS 学习笔记
广度优先搜索 BFS 学习笔记 引入 广搜是图论中的基础算法之一,属于一种盲目搜寻方法. 广搜需要使用队列来实现,分以下几步: 将起点插入队尾: 取队首 \(u\),如果 $u\to v $ 有一条路 ...
- 深度优先搜索 DFS 学习笔记
深度优先搜索 学习笔记 引入 深度优先搜索 DFS 是图论中最基础,最重要的算法之一.DFS 是一种盲目搜寻法,也就是在每个点 \(u\) 上,任选一条边 DFS,直到回溯到 \(u\) 时才选择别的 ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- 2014年暑假c#学习笔记目录
2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...
随机推荐
- selenium+java多层级frame切换的问题
关于selenium多层iframe切换,及iframe没有id和name属性的情况下进行切换的问题.(如下图:) 问题: 1. 在切入到frame:left中后,直接切换其他同级和上级frame报错 ...
- 安装vs2017后,RDLC 报表定义具有无法升级的无效目标命名空间
原先的RDLC报表定义用的命名空间是2008,用vs2017报表设计器重新保存后,会自动升级成2016,导致无法使用. 不想升级控件,太麻烦,所以就手动修改RDLC文件吧. 1.修改http://sc ...
- ROC曲线与AUC
一.ROC曲线 1.简介 ROC曲线全称是"受试者工作特征曲线 "(Receiver Operating Characteristic curve),又称为感受性曲线(Sensit ...
- Altium 中PCB的Gerber生产资料的输出详细步骤
生产文件的输出,俗称Gerber out,Gerber文件是所有电路设计软件都可以产生的文件,在电子组装行业又称为模版文件(Stencil Data),在PCB制造业又称为光绘文件.可以说Gerber ...
- renren_fast性能测试平台的安装部署
1.从GitHub下载源码: https://github.com/zyanycall/stressTestPlatform git clone https://github.com/zyanycal ...
- Mysql读写分离——主从数据库+Atlas
mysql集群 最近在参加项目开发微信小程序后台,由于用户数量巨大,且后台程序并不是很完美,所以对用户的体验很是不友好(简单说就是很卡).赶巧最近正在翻阅<大型网站系统与Java中间件实践> ...
- HTML学习1-Dom之事件绑定
事件: 1.注册事件 a. <div onxxxx=””></div> b. document .onxxxx= function() //找到这个标签 2.this,触发 ...
- Java中的==符号与equals()的使用(测试两个变量是否相等)
Java 程序中测试两个变量是否相等有两种方式:一种是利用 == 运算符,另一种是利用equals()方法. 当使用 == 来判断两个变量是否相等时,如果两个变量是基本类型变量,且都是数值类型(不一定 ...
- PHP XXE漏洞
PHP xml 外部实体注入漏洞(XXE) 1.环境 PHP 7.0.30Libxml 2.8.0Libxml2.9.0 以后 ,默认不解析外部实体,对于PHP版本不影响XXE的利用 2.原理介绍 X ...
- Thunder——Final冲刺中间产物
版本控制: http://www.cnblogs.com/lick468/p/7994015.html 软件功能说明书: http://www.cnblogs.com/szjzsd/p/7979565 ...