图论算法

图论--最大团问题

こ雲淡風輕ζ 提交于 2019-12-14 22:45:15
一、定义 一个无向图 G=(V,E),V 是点集,E 是边集。取 V 的一个子集 U,若对于 U 中任意两个点 u 和 v,有边 (u,v)∈E,那么称 U 是 G 的一个完全子图。 U 是一个团当且仅当 U 不被包含在一个更大的完全子图中。 G的最大团指的是定点数最多的一个团。 简单来说,极大团是增加任一顶点都不再符合定义的团,最大团是图中含顶点数最多的极大团,最大独立集是除去图中的团后的点集,而最大团问题就是在一个无向图中找出一个点数最多的完全图。 二、常用结论 1、最大团点的数量=补图中最大独立集点的数量 2、二分图中,最大独立集点的数量+最小覆盖点的数量=整个图点的数量 3、二分图中,最小覆盖点的数量=最大匹配的数量 4、图的染色问题中,最少需要的颜色的数量=最大团点的数量 三、算法实现 毕竟是NP完全问题,所以具体使用,什么算法,区别不是很大,具体体现在剪枝上! 对于弦图来说,求最大团一般使用 MCS 算法,而对于一般图来说,常使用 Bron-Kerbosch 算法 【Bron-Kerbosch 算法】 Bron-Kerbosch 算法用于计算图中的最大的全连通分量,即计算图的最大团。 1.算法原理 Bron-Kerbosch 算法的基础形式是一个递归回溯的搜索算法,其通过给定三个集合:R、P、X 来递归的进行搜索 初始化集合 R、X 分别为空,集合 P 为所有顶点的集合

[图论]Tarjan算法复习

柔情痞子 提交于 2019-12-03 14:16:32
1.模板+例题 P2341 [HAOI2006]受欢迎的牛|【模板】强连通分量 #include<cstdio> #include<cstring> #include<stack> #define N 500000 using namespace std; void in(int &read){ int x=0,f=1;char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-'){f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} read=x*f;//可以处理负数的读入优化 } bool instack[N]; stack <int> q; struct node{ int from; int to; int next; }e[N]; int head[N],num,tot,ans; void addedge(int from,int to) { e[++num].from=from; e[num].next=head[from]; e[num].to=to; head[from]=num; } int n,m,x,y,dfn[N],low[N],all[N],id[N]

图论基础——最短路算法集锦

故事扮演 提交于 2019-12-03 05:17:33
最短路算法有个 基础 —————— 松弛操作 (在大多数 最短路 算法都会涉及) if(d[e[i].v]>d[e[i].u]+w[i])//如果这条边的终点到源点的距离大于起点到源点距离,就替换。 { d[e[i].v]>d[e[i].u]+w[i]; } 最短路算法 一共有多少种方法 我不知道,在这里我只想记录 4 种: •Dijkstra:求单源点最短路( 不含负边权 ) •Bellman-ford:求单源点 最短路 ( 可含负边权 ) •SPFA(使用 队列 优化后的 Bellman-ford ) •Floyd:求各点间的最短路( 可含负边权 ) Firstly, Bellman-Ford算法 •算法步骤: 1、初始化:将除源点外的所有顶点最短距离估计值d[v]=inf,d[s]=0; 2、迭代求解:反复对边集E中每条边进行松弛操作,使得顶点集V中每个顶点v的最短距离估计值逐步逼近其最短距离; 3、检验负权回路:如果有存在点v,使得d[v]>d[u]+w[u][v],则有负权回路,返回false; 4、返回true,源点到v的最短距离保存在d[v]中。 那就上一个代码模版吧~ void Bellman() { for(int j=1;j<=n-1;j++)//每一次循环遍历所有的节点,遍历n-1次 { k=0 ; //判断每一次遍历中是否有过松弛,若没有

图论总结tarjan算法

匿名 (未验证) 提交于 2019-12-03 00:41:02
tarjan算法,是一个可以在有向图中找到强连通分量的的算法。 首先你要了解什么是强连通,以及什么是强连通分量。 下面是我给的简释: 一、强连通。 二、强连通子图。 三、强连通分量 极大的强连通子图。 解释: 强连通分量 百度百科 好的,那么现在就有一个问题: 输入:一个图有向图。 输出:它每个强连通分量。 用tarjan算法怎么实现呢? 先给出几个变量的意义: 1.DFN[i]表示第几个搜索到的; 2.LOW[i]表示从搜索的路返回,最多能走到哪里。 3.STACK[i]表示一个栈。 输出了一个图,长这样: xiam下面几张图是深搜的过程:(假设我们从第一个点开始搜) 如下图:搜到6,走不动了,开始回溯。然后6出栈。6节点就是一个强连通分量。 于是 有了下图:回溯到3节点,操作不动了,3出栈。三就是一个强连通分量。 4走过了,于是继续操作至下图: 回溯到2,然后三走过,继续走5,五再继续操作,6走过且有DFN[6]!=LOW[6],走1; 发现DFN[1]=LOW[1] 可以说明找到环了,就找到了一个强连通分量 LOW[4]=1; 然后还有一个栈内的搜索图,就是什么时候出栈,这个你可以自己画一下,就可以明白了。 然后最后栈里面剩下来的,就是所求的qian强连通分量。 好,下面以之前的那一道例题作为实战:(就是之前的那一道题) 输入n,m; 表示有n个点,m条边,下面输入m条边

算法与数据结构――图论

匿名 (未验证) 提交于 2019-12-03 00:19:01
C++实现及理论 https://www.cnblogs.com/polly333/category/720001.html https://github.com/liuyubobobo/Play-with-Algorithms python实现 https://github.com/ShiveryMoon/Imooc-Algorithm-PythonEdition 图的表达方式:邻接矩阵(适合表示稠密图(边的个数))、邻接表(适合表示稀疏图(边的个数远远小于所能拥有的最大的边的个数)) 邻接表实现: # -*- coding: utf-8 -*- import sys #由于python的面向对象特性,将定义一个Vertex类,所有与节点有关的属性都储存在vertex类中,比如节点id,节点邻居,节点的前继节点,节点是否被遍历过等等 #注意,此时我们定义的邻接表已经可以存储权重了,也就是说,这个邻接表已经适用于有权无权有向无向图,所以第八章将不会再新实现一个邻接表。 class Vertex(object): def __init__(self,key): self.id = key self.connectedTo = {} #key是vertex对象,value是两个对象之间的边的权重 self.ccid = 0 #Connected Components id

图论算法总结(一)――图的遍历

匿名 (未验证) 提交于 2019-12-03 00:09:02
关于数据结构,主要是有关树和图是最终的难点和痛点,关于算法,记住名字很简单,记住原理要花一点时间,如何内化为自己本身的知识,以及,在脑中有思路,随拿随用,这个需要特定的记忆方式。如果不能即拿即用,并不能说自己了解这个算法。建议大家,以自己的思维为中心,记住该思维的逻辑的同时,也记住该思路的代码,把算法内化为自己的修养。 参考博文:) 按照深度优先方式访问未访问的节点 dfs ( u ){ vis [ u ]= true ; for (从 u 出发能到达的所有顶点 v ) if ( vis [ v ]== false ) dfs ( v ); } dfsTrave ( G ){ for ( G 的所有节点 u ) if ( vis [ u ]== false ) dfs ( u ) } //邻接矩阵 void dfs ( int u ){ vis [ u ]= 1 ; printf ( "%d" , u ); for ( int i = 0 ; i < vertxNum ; i ++) if ( vis [ i ]== 0 && path [ u ][ i ]== 1 ) dfs ( i ); } void dfsTrave (){ for ( int i = 0 ; i < vertxNum ; i ++) if ( vis [ i ]== false ) dfs ( i ); }

【图论】最短路算法题解1

梦想与她 提交于 2019-12-02 10:50:46
51nod 1459 迷宫游戏 ###题目描述### 给定n个点,m条边,起点,终点,每个点有一个点权,给出走每条边所需的时间。 要求从起点尽快到达终点,使得获得的点权之和最大,输出最大得分。 ###题解### 在dijkstra算法中增加一个判断,从u到v的距离更新时,若有d[v]==d[u]+cost[u][v],更新v的值。 #include<cstdio> #include<map> #include<queue> #include<iostream> #include<vector> using namespace std; typedef pair<int,int> P; const int maxm=250000; const int maxn=502; const int INF=1e9; int n,m,start,end; int head[maxn]; int d[maxn],val[maxn],score[maxn]; int pos; int pre[maxn]; struct node{ int to,next,cost; }e[maxm]; void add(int x,int y,int z) { e[++pos].to=y; e[pos].cost=z; e[pos].next=head[x]; head[x]=pos; } int ans;

图论篇2——最小生成树算法

早过忘川 提交于 2019-12-02 07:03:46
基本概念 树 (Tree) 如果一个无向连通图中不存在回路,则这种图称为树。 生成树 (Spanning Tree) 无向连通图G的一个子图如果是一颗包含G的所有顶点的树,则该子图称为G的生成树。 生成树是连通图的极小连通子图。这里所谓极小是指:若在树中任意增加一条边,则将出现一条回路;若去掉一条边,将会使之变成非连通图。 最小生成树 一个 带权值 的 连通图 。 用$n-1$条边把$n$个顶点连接起来,且连接起来的 权值最小 。 应用场景 设想有9个村庄,这些村庄构成如下图所示的地理位置,每个村庄的直线距离都不一样。若要在每个村庄间架设网络线缆,若要保证成本最小,则需要选择一条能够联通9个村庄,且长度最小的路线。 Kruskal算法 知识点: 数据结构——并查集 基本思想 始终选择当前可用、 不会(和已经选取的边)构成回路 的最小权植边。 具体步骤: 1. 将所有边按 权值 进行 降序 排序 2. 依次选择权值最小的边 3. 若该边的两个顶点落在不同的连通分量上,选择这条边,并把这两个顶点标记为同一连通分量;若这条边的两个顶点落到同一连通分量上,舍弃这条边。反复执行2,3,直到所有的都在同一连通分量上。【这一步需要用到上面的并查集】 模板题: https://www.luogu.org/problem/P3366 #include <iostream> #include

【算法总结】图论算法(最小生成树和最短路)

瘦欲@ 提交于 2019-12-02 05:42:28
最小生成树和最短路算法   是很久以前就学过的东西。图论最基础的算法。通常在各种题目中担任题解的基础部分(这道题先跑个生成树再balabala)   这次也是复习了。 最小生成树   最小生成树是用来解决用最小的代价用N-1条边连接N个点的问题。常用的算法是Prim和Kruskal,两者时间复杂度并没有差很多(Prim堆优化的前提下),但是因为写Prim就要手撕堆所以我比较偏向Kruskal。    没错我不会(can't)用sort以外的STL Prim   Prim 算法使用和 Dijkstra 相似的蓝白点思想,用 dis 数组来表示 i 点与白点相连的最小权值,每一轮取出 dis 最小的蓝点,将其变为白点并修改与其相连的蓝点的 dis 值。    n 次循环,每次循环 Prim 算法都能让一个新的点加入生成树,n 次循环就能把所有点囊括到其中;每次循环 Prim 算法都能让一条新的边加入生成树,n-1 次循环就能生成一棵含有 n 个点的树;每次循环 Prim 算法都取一条最小的边加入生成树,n-1 次循环结束后,我们得到的就是一棵最小的生成树。这就是 Prim 采取贪心法生成一棵最小生成树的原理。   朴素Prim的时间复杂度是O(n²),显然的可以使用堆优化来获得更好的时间复杂度。 Kruskal   Kruskal 算法先将所有点认为是孤立的,然后将边按权值排序

图论 - Bellman-Ford算法

混江龙づ霸主 提交于 2019-11-30 16:14:49
Bellman-Ford Dijkstra算法虽好,但是不能解决带有负边权的图. 而利用Bellman-Ford可以完美的解决最短路和负边权的问题 朴素Bellman-Ford算法 w[i] 权值 u[i]->v[i] 存储边集 默认大家已经会了邻接表存储,如果有没有学会邻接表存储的小伙伴要先去学习一些邻接表的存储操作哦! ^_^ 核心代码: for(int k = 1; k <= n-1; k++) for(int i = 1; i <= m; i++) if(dis[v[i]] > dis[u[i]] + w[i]) dis[v[i]] = dis[u[i]] + w[i]; 显然其时间复杂度为O(m*n) 分析过程 下面列出一个具体的松弛过程可帮助大家更好的理解代码: 完整代码: #include <iostream> #include <algorithm> using namespace std; int main() { int dis[10], n, m, u[10], v[10], w[10]; int inf = 9999999; cin >> n >> m; //读入边 for (int i = 1; i <= m; i++) cin >> u[i] >> v[i] >> w[i]; //初始化dis数组 fill(dis, dis + 10, inf);