QBXT Day 5图论相关

图论是NOIP的一个非常重要的考点,换句话说,没有图论,NOIP的考纲就得少一大半(虽然很NOIP没有考纲)

图论这玩意吧,和数论一样是非常变态的东西,知识点又多又杂,但是好在一个事,他比较直观比较好想

对于一张图而言,我们定义图是一种由边和点构成的的一个玩意(其实是严谨定义我记不住了QWQ,但是不影响学习)

一般来说,图的存储难度主要在记录边的信息
无向图的存储中,只需要将一条无向边拆成两条即可
邻接矩阵:用一个二维数组 edg[N][N] 表示
edg[i][j] 就对应由 i 到 j 的边信息
edg[i][j] 可以记录 Bool,也可以记录边权
缺点:如果有重边有时候不好处理
空间复杂度 O(V^2)

点度等额外信息也是很好维护的

#include <bits/stdc++.h>using namespace std;const int N = 5005;int ideg[N], odeg[N], n, m, edg[N][N];
bool visited[N];void travel(int u, int distance)
{cout << u << " " << distance << endl; visited[u] = true;for (int v = 1; v <= n; v++)if (edg[u][v] != -1 && !visited[v])//是否已经访问过 travel(v, distance + edg[u][v]); //if there is an edge (u, v) and v has not been visited, then travel(v)
}
int main()
{cin >> n >> m;memset(edg, -1, sizeof edg);memset(visited, false, sizeof visited);for (int u, v, w, i = 1; i <= m; i++)cin >> u >> v >> w, edg[u][v] = w, odeg[u]++, ideg[v]++;//出度和入度 for (int i = 1; i <= n; i++)cout << ideg[i] << " " << odeg[i] << endl;for (int i = 1; i <= n; i++)if (!visited[i]) travel(i, 0);
}/*
Given a graph with N nodes and M unidirectional edges.
Each edge e_i starts from u_i to v_i and weights w_i
Output a travelsal from node 1 and output degree of each node.
*/

其实这个英文注释也还蛮不错的啊

邻接矩阵本质上其实就是一个二维数组,它在存储一个稠密图的时候效率比较好,但是稀松图的话就非常浪费空间

所以我们就没有必要用二维数组记录信息,我们只需要对每一个点记录他的出边就行

这样记的话,复杂度就是他的边数

对每一个点 u 记录一个 List[u],包含所有从 u 出发的边
直接用数组实现 List[u]?读入边之前不知道 List[u] 长度
手写链表(链式前向星)
用 STL 中的 vector 实现变长数组,当然你想要手写指针也没问题
只需要 O(V + E) 的空间就能实现图的存储(边数加点数)

其实写这个链表存储0有很多方式啊,你可以用指针,手写指针,也可以用vector ,还可以用数组毛模拟

我们详细理解一下代码

#include <bits/stdc++.h>using namespace std;const int N = 5005;struct edge {int u, v, w; edge *next;//next指针指向 edge(int _u, int _v, int _w, edge *_next):u(_u), v(_v), w(_w), next(_next) {}
};
edge *head[N]; //List[u] 最前面的节点是谁 
int ideg[N], odeg[N], n, m;
bool visited[N];void add(int u, int v, int w)
{edge *e = new edge(u, v, w, head[u]);head[u] = e;
}
void travel(int u, int distance)
{cout << u << " " << distance << endl; visited[u] = true;for (edge *e = head[u]; e ; e = e -> next)if (!visited[e -> v])travel(e -> v, distance + e -> w); //if there is an edge (u, v) and v has not been visited, then travel(v)
}
int main()
{cin >> n >> m;memset(visited, false, sizeof visited);memset(head, 0, sizeof head);for (int u, v, w, i = 1; i <= m; i++)cin >> u >> v >> w, add(u, v, w), odeg[u]++, ideg[v]++;for (int i = 1; i <= n; i++)cout << ideg[i] << " " << odeg[i] << endl;for (int i = 1; i <= n; i++)if (!visited[i]) travel(i, 0);
}/*
Given a graph with N nodes and M unidirectional edges.
Each edge e_i starts from u_i to v_i and weights w_i
Output a travelsal from node 1 and output degree of each node.
*/

但是我个人是不用指针的,因为可能还是不习惯的原因吧,而且指针的写法并没有什么特别的优点

还有一个数组模拟版本

#include <bits/stdc++.h>using namespace std;const int N = 5005;struct edge {int u, v, w, next;
}edg[N];
int head[N]; //List[u] stores all edges start from u
int ideg[N], odeg[N], n, m, cnt; //cnt: numbers of edges
bool visited[N];void add(int u, int v, int w)
{int e = ++cnt;edg[e] = (edge){u, v, w, head[u]};head[u] = e;
}
void travel(int u, int distance)
{cout << u << " " << distance << endl; visited[u] = true;for (int e = head[u]; e ; e = edg[e].next)if (!visited[edg[e].v])travel(edg[e].v, distance + edg[e].w); //if there is an edge (u, v) and v has not been visited, then travel(v)
}
int main()
{cin >> n >> m; cnt = 0;memset(visited, false, sizeof visited);memset(head, 0, sizeof head);for (int u, v, w, i = 1; i <= m; i++)cin >> u >> v >> w, add(u, v, w), odeg[u]++, ideg[v]++;for (int i = 1; i <= n; i++)cout << ideg[i] << " " << odeg[i] << endl;for (int i = 1; i <= n; i++)if (!visited[i]) travel(i, 0);
}/*
Given a graph with N nodes and M unidirectional edges.
Each edge e_i starts from u_i to v_i and weights w_i
Output a travelsal from node 1 and output degree of each node.
*/

但是数组模拟必然是逃不开浪费时间过多的,这个事就很讨厌了,邻接矩阵以其优秀的可读性以及构造性换来了不少空间,唉

我个人现在是这样的,判断变数和点数的值,如果差别较大,那么出题人可能是想构造菊花树之类的,差别较小就意味着稠密,那么写邻接矩阵更节省时间(前提是你两个都能用)

还有一种写法是用vector

抛去邻接矩阵不讲,如果我们用edg[u][i]表示从u出发的第i条边,这样实际上还是O(n^2)的,所以我们要用一个能够自己改变长度的STL,这样能让空间最大化

#include <bits/stdc++.h>using namespace std;const int N = 5005;struct edge {int u, v, w;
};
vector<edge> edg[N]; //edge记录变长数组记录的是什么类型 
int ideg[N], odeg[N], n, m, cnt; //cnt: numbers of edges
bool visited[N];void add(int u, int v, int w)
{edg[u].push_back((edge){u, v, w});//一个强制类型转换 
}
void travel(int u, int distance)
{cout << u << " " << distance << endl; visited[u] = true;for (int e = 0; e < edg[u].size(); e++)//遍历边 if (!visited[edg[u][e].v])//以u出发的第e条出边 travel(edg[u][e].v, distance + edg[u][e].w); //if there is an edge (u, v) and v has not been visited, then travel(v)
}
int main()
{cin >> n >> m; cnt = 0;memset(visited, false, sizeof visited);for (int u, v, w, i = 1; i <= m; i++)cin >> u >> v >> w, add(u, v, w), odeg[u]++, ideg[v]++;for (int i = 1; i <= n; i++)cout << ideg[i] << " " << odeg[i] << endl;for (int i = 1; i <= n; i++)if (!visited[i]) travel(i, 0);
}/*
Given a graph with N nodes and M unidirectional edges.
Each edge e_i starts from u_i to v_i and weights w_i
Output a travelsal from node 1 and output degree of each node.
*/

要注意的是,c++的STL数组默认都是以0为结尾的、

vector是这样构造的

<>里面写的是变量类型,可以是int 或者float或者结构体

生成树

我们考虑一个联通的无向图,我们考虑找出这个图当中的子图(点的数量是一样的,可以删掉边)

给定一个连通无向图 G = (V; E)
E′ ⊂ E
G′ = (V; E′) 构成一棵树
G′ 就是 G 的一个生成树

而且我们可以发现生成树不是唯一的,而且我们可以知道的是生成树的数量是指数级别的

那么最小生成树其实就是生成树当中最大边权的值最小

 怎么求呢?

Algorithms for Minimal Spanning Tree:
Kruskal
Prim
Kosaraju

Kruskal

克鲁斯卡尔的思想是贪心加上并查集

我们只把所有的边的信息存下来,而不用存图(因为最小生成树只问你最小边权和之类的问题,而不文)

,对于所有的边权进行排序,找到当前边权最小的边 e : (u; v)
如果 u 和 v 已经连通,则直接删除这条边(这里的判断就是用并查集的思想,如果最终并查集的指向指到了一个同一个点,那么就是联通的啊)
如果 u 和 v 已经未连通,将之加入生成树
重复上述过程

这个称为Rigorous proof(消圈算法)

Kruskal的证明方法很迷啊,就感性理解一下就好

毕竟贪心这东西证明正确性还是挺困难的。

Prim的做法是,我们找一个连通块,我们把和这个连通块最短的点加到连通块当中去(这俩都可以用堆优化)

Kosaraju的做法是,我们有很多连通块,然后第一轮的时候对于每一个连通块找到和它相连的最短的边,就把这两个集合连接起来

 

转载于:https://www.cnblogs.com/this-is-M/p/10806609.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/386887.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RUNOOB python练习题47 交换两个变量值

用来练手的python练习题&#xff0c;原题链接: python练习实例47 题干: 两个变量值互换 在C语言C中我们要构造一个能交换两个变量值的函数很方便&#xff0c;我们可以使用指针&#xff0c;或者C中的引用。那么在没有指针的python中如何构造一个可以交换两个变量值的函数呢&am…

lambda函数 RUNOOB python练习题49

用来练手的python练习题&#xff0c;原题链接python练习实例49 该练习题主要是关于lambda函数的使用方法&#xff0c;本文就python中的lambda函数做出一点总结。 1. lambda函数的定义与调用 在python中&#xff0c;我们都知道使用def关键词来定义一个函数, 例如一个最简单的…

kubernetes(k8s)安装部署

Kubernetes是一个开源的&#xff0c;用于管理云平台中多个主机上的容器化的应用&#xff0c;Kubernetes的目标是让部署容器化的应用简单并且高效,Kubernetes提供了应用部署&#xff0c;规划&#xff0c;更新&#xff0c;维护的一种机制。 Kubernetes一个核心的特点就是能够自主…

python random随机数 RUNOOB python练习题50

用来练手的python练习题&#xff0c;原题链接: python练习实例50、 该练习题主要包含了random模块随机数的应用&#xff0c;下面给出几个常用的模块内函数。 1. 生成浮点型随机小数 最简单的&#xff0c;就是用random函数&#xff0c;生成 [0.0,1.0)[0.0, 1.0)[0.0,1.0)范围…

Spring Cloud Eureka Consul使用和对比

Spring Cloud简介 最大的区别是Eureka保证AP, Consul为CP。 Consul强一致性(C)带来的是&#xff1a; 服务注册相比Eureka会稍慢一些。因为Consul的raft协议要求必须过半数的节点都写入成功才认为注册成功 Leader挂掉时&#xff0c;重新选举期间整个consul不可用。保证了强一致…

符号 RUNOOB python练习题 51

用来练手的python练习题&#xff0c;原题链接: python练习实例51 python中的 & 和 | 使用过程中&#xff0c;变量类型不同&#xff0c;这两个符号的作用也不同。 1. 对于数字变量&#xff0c;&\&& 和 ∣|∣ 用于逐位运算 # 二进制逐位逻辑与门运算 a 0b110…

Python打印杨辉三角形 RUNOOB python练习题61

用来练手的python练习题&#xff0c;原题链接: python练习实例61 题干: 打印出杨辉三角形 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1 1 9 36 84 126 126 84 36 9 1 实现代码如下: import numpy as nptable…

使用Docker快速搭建Tensorflow开发环境

当我刚开始学习使用scikit-learn时&#xff0c;总是会出现各种各样的包依赖问题&#xff0c;兜兜转转了一遍才全部安装好&#xff0c;现在的机器学习算法开发者大都使用tensorflow、pytorch来实现自己的想法&#xff0c;但依然会面临各种包版本和依赖的问题&#xff0c;有一段时…

RUNOOB python 67 数组的元素互换

用来练手的Python练习题&#xff0c;原题链接:python练习实例67 题干: 输入数组&#xff0c;最大的与第一个元素交换&#xff0c;最小的与最后一个元素交换&#xff0c;输出数组 代码如下: import numpy as nptable np.array([10,4,9,3,11,25,37,15,2,231,672,22]) #定义sw…

微信小程序、微信公众号、H5之间相互跳转

一、小程序和公众号 答案是&#xff1a;可以相互关联。 在微信公众号里可以添加小程序。 图片有点小&#xff0c;我把文字打出来吧&#xff1a; 可关联已有的小程序或快速创建小程序。已关联的小程序可被使用在自定义菜单和模版消息等场景中。 公众号可关联同主体的10个小程…

数组元素前移后移 RUNOOB python练习题 68

用来练手的python练习题&#xff0c;原题链接: python练习实例68 题干: 有 n 个整数&#xff0c;使其前面各数顺序向后移 m 个位置&#xff0c;最后 m 个数变成最前面的 m 个数 代码如下: import numpy as np # 构造一个储存了n个整数的numpy数组 def numbers_input(n):a n…

Mac Eclipse安装lombok

Lombok是一个可以通过注解的形式可以帮助消除一些必须但是显得很臃肿的Java代码的工具&#xff0c;通过使用对应的注解&#xff0c;可以在进行编译源码的时候生成对应的方法&#xff0c;比如类属性的get/set/toString()/类的构造方法等. 下面记录一下在Mac Eclipse是如何安装Lo…

tf.reduce_sum()方法深度解析

首先看一下reduce_sum及其参数的注释 : def tf.reduce_sum(input_tensor, axisNone, keepdimsFalse, nameNone) Computes the sum of elements across dimensions of a tensor. Reduces input_tensor along the dimensions given in axis. Unless keepdims is true, the rank o…

主成分分析(PCA)原理详解_转载

一、PCA简介 1. 相关背景 在许多领域的研究与应用中&#xff0c;往往需要对反映事物的多个变量进行大量的观测&#xff0c;收集大量数据以便进行分析寻找规律。多变量大样本无疑会为研究和应用提供了丰富的信息&#xff0c;但也在一定程度上增加了数据采集的工作量&#xff0c;…

Mac cnpm装包时提示Error: EACCES: permission denied解决办法

Cnpm装包时提示Error: EACCES: permission denied解决办法 2018年03月04日 09:31:51 miniminixu 阅读数&#xff1a;1598 版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 https://blog.csdn.net/miniminixu/article/details/79434609 只需在cnpm …

特征点检测 FAST算法及代码详解

本文着重介绍了用于图像特征点检测的算法&#xff0c;FAST算法&#xff0c;以及使用matlab的实现。 FAST算法是一种拐点检测算法&#xff0c;其主要应用于提取图像中的特征点&#xff0c;在动态成像的一系列图像中追踪定位对象。众所周知&#xff0c;我们生活的世界是动态化的…

一文看懂计算机神经网络与梯度下降

1. 计算机神经网络与神经元 要理解神经网络中的梯度下降算法&#xff0c;首先我们必须清楚神经元的定义。如下图所示&#xff0c;每一个神经元可以由关系式yf(∑i1nwixib)y f(\sum_{i1}^nw_ix_i b)yf(∑i1n​wi​xi​b)来描述&#xff0c;其中X[x1,x2,...,xn]X [x_1,x_2,..…

vs2015web项目无法加载64位c++的dll,提示试图加载不正确的格式

vs2015无法加载64位c的dll&#xff0c;提示试图加载不正确的格式&#xff01; 开始用winform引用64位的c的dll&#xff0c;在项目的属性设置生成里面选择any cpu或者x64都可以成功! 但在web项目和接口里面运行就提示试图加载不正确的格式&#xff0c;想办法找了一天也没处理掉&…

使用Rancher搭建K8S测试环境

环境准备&#xff08;4台主机&#xff0c;Ubuntu16.04Docker1.12.6 SSH&#xff09;&#xff1a; rancher1 192.168.3.160 只做管理节点 node1 192.168.3.161 K8S的节点1 node2 192.168.3.162 K8S的节点2 node3 192.168.3.163 K8S的节点3 此时如…