首页 > 其他分享 >图论2

图论2

时间:2023-06-30 23:11:16浏览次数:35  
标签:图论 return int 路径 结点 vis dis

内容来自紫书、2019 wannafly camp、进阶指南、算法导论,可能根本看不完,可能这一切都没有意义。

图论

以前学算法的时候,很少关心算法正确性的证明,和传统的理科课程如高数课这一类课差得太远了。参加编程竞赛时每一份代码就像黑盒,没有方向感,看起来就像另外一种应试。

图的存储

广度优先搜索、深度优先搜索

最小生成树

(待完成)

拓扑排序

拓扑排序是将有向无环图的所有结点排一个顺序。

对于一个有向无环图 G=(V,E) 来说,拓扑排序是指如果图 G 包含边 (u,v) ,则拓扑排序中,结点 u 在结点 v 之前出现。

12345只需要枚举所有结点,执行深度优先搜索。有两种方法:

1)后序遍历:深度优先搜索完成后把结点放在拓扑序首部

2)前序遍历:深度优先搜索开始前把结点放在拓扑序尾部

第二种方法是错的,因为不能保证图是一棵树,反例如图:

采用第二种方法得到的结果是12534,2和5在4之前,和定义矛盾了。

还有如果已经放进拓扑序就不能再放了。

1 int vis[maxn]; //访问标志

2 int topo[maxn], t;

3 bool dfs(int u) { //单次深度优先搜索,后序遍历

4 vis[u] = -1;

5 for(int i=hd[u]; ~i; i=nxt[i]) {

6 int v=to[i];

7 if(vis[v] == -1) return false; //存在有向环,失败推出

8 if(!vis[v] && !dfs(v)) return false; //后序遍历

9 }

10 vis[u]=1; topo[--t]=u;

11 return true;

12 }

13 bool toposort() {

14 t=n;

15 memset(vis, 0, sizeof(vis));

16 for(int u=0; u<n; u++) if(!vis[u])

17 if(!dfs(u)) return false;

18 return true;

19 }

最短路的一些性质

δ(s,v) 表示从结点s到结点v之间所有路径最小的长度。

1)三角形不等式:对于所有的u,v, δ(s,v)δ(s,u)+w(u,v)

(待完成)

单源最短路

最简单的情况

边长为1:直接BFS

边长为0或1:BFS(加一个优先级)

边长为0或1或2:拆边然后BFS,但是这好像没有意义

Dijkstra

边长为非负数,BFS的队列换成优先队列(每次选择离起点距离最小的点进行扩展)。

1)松弛操作

对每个结点 v ,记录从起点到 v 的最短路径的上界,记为 v.d ,还需要记录 v 的前驱结点 v.π 。刚开始的时候,起点s的最短路径上界为0。除了起点外最短路径上界为+∞。所有结点的前驱结点为空。

1 RELAX(u,v,w): //用边w和结点u去更新结点v的最短路径上界和前驱结点

2 if v.d > u.d + w(u,v):

3 v.d = u.d + w(u,v)

4 v.π = u

2)DIJKSTRA

1 DIJKSTRA(G,w,s):

2 INITIALIZE()

3 //S=∅ //已经确定了最短路径长度的结点

4 Q={s}

5 while Q!=∅:

6 u = EXTRACT_MIN(Q) //选出Q中到起点s路径长度最短的结点

7 //S = S ∪ {u} //根据数学归纳法,此时u的路径长度一定最短

8 for v in G.Adj[u] //枚举和u相邻的结点

9 RELAX(u,v,w)

10 Q = Q ∪ {v}

上面的算法没有规定EXTRACT_MIN是如何实现的。如果利用二叉堆等数据结构选择最小的点,时间复杂度是 O(n+mlogn) 。如果枚举所有点取最小值 ,时间复杂度是 O(n2+m) 。(详细证明看算法导论)

一种用C++的优先队列的实现:

1 void dijkstra(int s) {

2 std::priority_queue<

3 std::pair<int,int>, std::vector<int>, std::greater<int>

4 > pq; //保存(距离起点的长度,结点编号)的优先队列,(堆)向下调整的标准是长度大于子结点

5 pq.push(s);

6 while(!pq.empty()) {

7 int u=pq.top().second; pq.pop();

8 if(vis[u]) continue;

9 vis[u]=1; //u已经是最短

10 for(int i=hd[u]; ~i; i=nxt[i]) {

11 int v=to[i], w=len[i];

12 if(dis[u] + w < dis[v]) {

13 dis[v] = dis[u]+w;

14 prev[v] = u;

15 pq.push(std::make_pair(dis[v], v));

16 }

17 }

18 }

19 }

Bellman-Ford

边长可能为负数。

1)松弛操作

见上文

2)BELLMAN_FORD

这个算法的正确性需要由图中没有负环来保证,在没有负环的情况下,起点s到任意一个结点v的路径都没有环,路径的边数小于等于n-1(n是整个图的结点数)。第i次循环( 1in1 )能找出从s到v的边数小于等于i的最短路径。(详细证明看算法导论)。时间复杂度是 O(nm)

1 BELLMAN_FORD(G,w,s):

2 INITIALIZE() //初始化v.d=+∞,v. π=NULL,s.d=0

3 for i in range(n-1): //循环n-1次

4 for edge(u,v,w) in G: //枚举所有边(从u到v,长度是w)

5 RELAX(u,v,w) //用边w和结点u去更新结点v的最短路径上界和前驱结点

6

7 //判断负环(即走一圈长度和为负数的环),如果有负环,BELLMAN_FORD算法失效

8 for edge(u,v) in G:

9 if v.d > u.d+w(u,v):

10 return false

11 return true

3)用队列来优化(即“SPFA”,某些随机数据下非常快)

上面的算法没有规定怎么枚举所有边,有一种思路是只枚举被更新的结点引出的边,这样可以节省一些不必要的结点。每次更新完成一个结点v的属性后,就把v放进一个队列,下次就用这个结点引出的边来更新其他结点。当然最坏情况下的时间复杂度没有变,即 O(nm)

1 bool bellman_ford_q() { //"spfa"

2 std::queue<int> q;

3 memset(dis, 0x3f, sizeof(dis));

4 memset(vis, 0, sizeof(vis));

5 memset(cnt, 0, sizeof(cnt));

6 dis[0] = 0; vis[0] = 1;

7 q.push(0);

8 while(!q.empty()) {

9 int u=q.front(); q.pop();

10 vis[u] = 0;

11 for(int i=hd[u]; ~i; i=nxt[i]) {

12 int v=to[i], w=len[i];

13 if(dis[u]+w < dis[v]) {

14 dis[v] = dis[u]+w;

15 prev[v] = u;

16 if(!vis[v]) {

17 q.push(v), vis[v]=1; //如果已经在队列里面

18 /*判断是否有负环*/

19 }

20 }

21 }

22 }

23 return true;

24 }

4)判断负环的方法:

bellman-ford循环了n次后进行判断

入队次数>n(为什么?以后再看)

记录从起点到v的路径边数,>=n时就是有负环。

所有两两节点之间的最短路

1)Floyd

无负环的图,可以有负边权

for(int k=0; k<n; k++)

for(int i=0; i<n; i++)

for(int j=0; j<n; j++)

dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);

设dis[k][u][v]表示路径中间的点只在 {0,1,2,,k} 中的最短路径。每次都新考虑一个点。时间复杂度 O(n3)

差分约束系统

(待完成)

有向图的强连通分量

网络流

标签:图论,return,int,路径,结点,vis,dis
From: https://www.cnblogs.com/arith/p/17517998.html

相关文章

  • 牛客图论 (第一章)
    F棋盘覆盖#include<bits/stdc++.h>usingnamespacestd;#definelllonglongconstintN=105,M=N*N*4,ID=N*N+N;intn,m;inthead[ID],ver[M],nxt[M],tot;boolban[N][N];intdx[]={1,-1,0,0},dy[]={0,0,1,-1};intmatch[ID];boolv[ID];......
  • 【图论】【建模】IOI2016 railroad
    【图论】【建模】IOI2016railroad题目描述Anna在一个游乐园工作。她负责建造一个新的过山车铁路。她已经设计了影响过山车速度的\(n\)个特殊的路段(方便起见标记为\(0\)到\(n-1\))。现在Anna必须要把这些特殊的路段放在一起并提出一个过山车的最后设计。为了简化问题,你可......
  • 图论 学习笔记(省选+)
    网络流最大流问题(MaximumFlowProblem)有向有权图给定起点s和终点t预期:求出从s到t的最大流ps.有些“管道”达不到其最大容量朴素的匹配算法(NaiveAlgorithm):未必能找到最大流,其结果往往比最优解差一点,但是其他更好的算法都基于此算法构建一个和原图(OriginalGraph)相同......
  • 图论 学习笔记
    图的基本概念和数据结构圆圈表示节点线是边图是V和E的二元组无向图:边没有方向(边是双向的)有向图:边有方向无权图:所有边的权重都是1有权图:权重不同;在不同的应用里,权重的意义不同 没有的边记作0或者无穷大,具体看实际应用 基本原则是进行搜索的时候,使无法通过这条边数据结构......
  • 图论(未完成)
    -1.最短路径-1.1Bellman-FordBellman-Ford是一种最基础的求解单源最短路径的算法,其整体思想是暴力,但是用途十分广泛。具体实现中,该算法将\(m\)条边松弛\(n-1\)次,其中松弛是指对于一条边\((x,y)\),如果\(dis_x+w_{x,y}<dis_y\),那么将\(dis_y\)的值修改为\(dis......
  • 【图论】割点与桥
    目录定义割点割边(桥)关系算法求割点伪代码模版定义割点如果删除无向图中的某个点会使无向图的连通分量数增多,则把这个点称为割点。割边(桥)如果删除无向图中的某条边会使无向图的连通分量数增多,则把这个点称为割边(桥)。关系桥的两端可以有割点。算法求割点割点:存在子树最高......
  • 【P8819 [CSP-S 2022]】 星战 题解(图论 + 哈希)
    图论+哈希。Link.因为实在是太妙了所以写个题解。Solution因为每个点的出度都为\(1\),所以从任意一点出发永远可以走下去,故每次只需判断每个点度数是否为\(1\)即可。然后一三操作显然直接\(O(1)\)维护,\(50\pts\)。考虑二四操作。每次操作显然对点\(u\)的出度......
  • [Week 20]每日一题(C++,图论,数学,搜索)
    目录T1[Daimayuan]Collision(C++,多源最短路)题目描述输入描述输出描述样例输入1样例输出1样例输入2样例输处2数据范围解题思路T2[Daimayuan]农田划分(C++,数学,BFS)题目描述题目输入题目输出样例输入1样例输出1样例输入2样例输出2数据范围解题思路T3[Daimayuan]三段式(C++,数组前缀......
  • 离散数学(屈婉玲)第二版 第五部分 图论 总结
    第5部分  图论前言:图是我们日常生活中一个很常见的概念,我们学习时会画思维导图,思维导图有节点,有路线;生活中会用到地图导航,有起点有终点有路线。而图论中的图便是生活中以及数学中具象事物抽象化的体现。前言的前言:若有错误之处或不完整之处希望指出,虚心接受任何批评和建议!一.......
  • 3. 搜索与图论(I)
    3.1DFS(深度优先搜索)例题:AcWing842.排列数字题目:给你一个数\(n\),按字典序将长度为\(n\)的全排列全部输出。\(1\len\le9\)。思路:运用DFS暴力搜索即可,时间复杂度\(O(n!)\)。图3-1(图源:AcWing@Hasity)如图\(\texttt{3-1}\)展示了\(n=3\)时的搜索过程:初始时......