ACM图论基础算法

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
则存在负权回路;否则dis[v]即为s到v的最 短距离,pre[v]为前驱。
Bellman Ford算法适用范围
单源最短路径(从源点s到其它所有顶点v); 有向图&无向图(无向图可以看作(u,v),(v,u)同
属于边集E的有向图); 边权可正可负(如有负权回路输出错误提示); 差分约束系统(需要首先构造约束图,构造不
}
克鲁斯卡尔算法
kruskal 算法思想:贪心选取最短的边来 组成一棵最小的生成树。
具体做法:先将所有的边做排序,然后 利用并查集作判断来优先选择较小的边, 直到建成一棵生成树。
克鲁斯卡尔算法示例代码(a)
struct Msg//定义边结构体 { int s , e; int length; }Line[6000]; int FindFather(int v)//并查集 { if(father[v] == v) return v; else father[v] = FindFather(father[v]); return father[v]; }
if(!visit[j] && low[j] > dist[u][j] + minD)
low[j] = dist[u][j] + minD;
}
}
return cnt;
}
Bellman Ford算法描述
对每条边进行|V|-1次Relax操作; 如果存在(u,v)∈E使得dis[u]+w<dis[v],
算法思想
1.从有向图中选取一个没有前驱的顶 点,并输出之;
2.从有向图中删去此顶点以及所有以 它为尾的弧;
重复上述两步,直至图空,或者图不 空但找不到无前驱的顶点为止。没有前 驱 -- 入度为零,删除顶点及以它为尾的 弧-- 弧头顶点的入度减1。
根据算法思想: 找到的点的顺序依次为v6 , v1 , v4 , v3 , v2 , v5 (注意一边输出当前点,一边更新其他点的入度)
for(i = 1;i < size; ++ i){
minD = 0x3fffffff;
for(j = 1;j <= size; ++ j)
if(!visit[j] && minD > low[j]){
minD = low[j] ; u = j;
}
cnt += minD;
visit[u] = 1;
for(j = 1;j <= size; ++ j){
}
if (inf == minc) return -1; // 原图不连通
res += minc; vis[p] = 1;
for (j=0; j<n; j++)
if (0 == vis[j] && lowc[j] > cost[p][j])
lowc[j] = cost[p][j];
}
return res;
图的遍历方式
按照某种顺序访问图中的所有顶点, 这样的操作较遍历。常用的遍历方 式有: 深度优先遍历DFS 广度优先遍历BFS
拓扑排序
概念引入: 一个工程常被分为多个小的子工程,这 些子工程被称为活动(Activity),在有向 图中若以顶点表示活动,有向边表示活 动之间的先后关系,这样的图简称为 AOV网。在AOV网中为了更好地完成工程, 必须满足活动之间先后关系,需要将各 活动排一个先后次序即为拓扑排序。
图的存储方式—邻接矩阵
邻接矩阵使用场合
数据规模不大n <= 1000,m越大越好 稠密图最好用邻接矩阵 图中不能有多重边出现 在做网络流类型的题目时候多采用邻接
矩阵,因为网络流的复杂度高,本身就 要求数据规模不能太大,并且需要频繁 地更新矩阵中的数据。
图的存储方式—邻接表
邻接表
mp[i]用链表的方式存储顶点i的相邻结点 空间复杂度:有向图O(2m+n)
了floyd,也可以试一下)
最小生成树
prim 算法(复杂度O(n^2) kruskal算法复杂度O(m*lg(m)) 对于不同问题可以选择不同算法
Prim 算法证明
首先,一定有一个最优解包含了权值最小的边e_1 (prim的第一步),因为如果不是这样,那么最优的 解不包含e_1,把e_1加进去会形成一个环,任意去掉 环里比e_1权值大的一条边,这样就构造了更优的一个 解,矛盾。
int low[size];
bool visit[size];
memset(visit , 0 ,sizeof(visit));
visit[s] = 1;
int i , j , minD , cnt = 0;
for(i = 1;i <= size; ++ i) low[i] = dist[s][i];
差分约束例题(POJ 1201)
首先第一个转化,是找到一个合理的表示。用ti表示每 一个数,如果有用就是1,否则是0。吧S(i+1)定义成 S(i+1)=∑ tj (1<=j<=i)也就是。S[i+1]表示从0到i有 多少个数是需要的。
因此,题目中的条件可以表示成S[bi+1]>=S[ai]+Ci, 就可以将ai到bi+1连一条有向边,边权为Ci。
图论的基础算法小结
By yesterday @ BJTU
部分内容摘自HH的PPT
目录
图的存储 拓扑排序 最小生成树 最短路 强连通分量
图的存储——邻接矩阵
邻接矩阵
g[i][j] 表示顶点i和顶点j的边关系
是否有边相连 边权值
空间复杂度:O(n^2) 访问速度快、直观、适合稠密
等式时>=表示求最小值, 作为最长路,<=表 示求最大值, 作为最短路。<=构图时, 有负环 说明无解;求不出最短路(为Inf)为任意解。 >=构图时类似)。 这个算法一般可以被spfa代替
Spfa算法
这是Bellman Ford的改进算法 建立一个队列,初始时队列里只有起始点,在
建立一个表格记录起始点到所有点的最短路径 (该表格的初始值要赋为极大值,该点到他本 身的路径赋为0)。然后执行松弛操作,用队 列里有的点去刷新起始点到所有点的最短路, 如果刷新成功且被刷新点不在队列中则把该点 加入到队列最后。重复执行直到队列为空
用归纳法,假设prim的前k步选出来的边e_1,…, e_k是最优解的一部分,用类似的方法证明prim的方法 选出的e_k+1 一定也能构造出最优解
代码示例
#define typec int // type of cost
const typec inf = 0x3f3f3f3f; // max of cost
Dijkstra算法
Dijkstra(迪杰斯特拉)算法是典型的单源 最短路径算法,用于计算一个节点到其 他所有节点的最短路径。主要特点是以 起始点为中心向外层层扩展,直到扩展 到终点为止。注意该算法要求图中不存 在负权边。
Dijkstra算法代码示例
int dijkstra(int s){//s是源点
for (i=1; i<n; i++) lowc[i] = cost[0][i];
for (i=1; i<n; i++) {
minc = inf; p = -1;
for (j=0; j<n; j++)
if (0 == vis[j] && minc > lowc[j]) {
minc = lowc[j]; p = j;
期望的时间复杂度O(ke), 其中k为所有顶点进 队的平均次数,可以证明k一般小于等于2。
Spfa算法代码演示
queue <Node *> Q;
while(!Q.empty()) Q.pop();
memset(visit , 0 , sizeof(visit));
for(i = 2;i <= p; ++ i) dist[i] = inf;
dist[1] = 0;
Q.push(father[1]);
Hale Waihona Puke visit[1] = true;
Spfa算法代码演示
while(!Q.empty()){
Node *p = Q.front();
Q.pop();
visit[p->s] = 0;
while(p){
if(dist[p->s]+p->v < dist[p->e]){
拓扑排序的使用
判断一个有向图中是否有环。无环的图 所有点都能进行拓扑排序。
类似于课程安排问题的题目
拓扑排序代码示例
int Find()//找零入度点 {
for(int i = 0 ;i < n; ++ i) if(!Vis[i] && G[i]->degree == 0) { Vis[i] = 1; Grap *p = G[i]->next; while(p) {//更新其他节点 p->degree -- ; p = p->next; } return i; }
father[s] = e; ans += Line[i].length; }
建议的题目
Poj 2421 两种方法都尝试一下,使用克鲁斯卡尔
算法会简单一些
单源最短路径
描述:从一个点出发,到达其他顶点的 最短路径的长度。
算法dfs(POJ 1062) 算法dijkstra 算法bellman Ford 算法spfa
无向图O(4m+n) 对稀疏图可以减少存储空间 可以直接访问到一个点的相邻结点 图的信息一般都不做修改
图的存储方式—邻接表
邻接表使用场合
顶点数很多n>1000,边数却不多。
采用邻接表存储后,很多算法的复杂度也都 是跟边数有关。
连通性的问题很多情况边数不多,多采用邻 接表存储方式
邻接表code
int vis[V]; typec lowc[V];
typec prim(typec cost[][V], int n) // vertex: 0 ~ n-1
{
int i, j, p;
typec minc, res = 0;
memset(vis, 0, sizeof(vis));
vis[0] = 1;
return -1; }
void TopSort() {
for(int i = 0 ;i < n; ++ i) printf(“%d ” , Find());//输出拓扑排序结果
}
拓扑排序习题
校赛第四题(我没找到题号) 去年武汉区域赛第二题(可以到uva去刷
) Poj 1094(先用拓扑排序刷一下,等讲
一般情况下问题要求我们求得一组最优 解。
解决方案是建立一个有向图,然后用 Bellman Ford或是spfa来解决
有两种建图方法,两种建图只要会一种 即可
差分约束例题(POJ 1201)
问题描述:已知n (1 <= n <= 50000)个 区间,每个区间范围在[a , b] (0 ≤ a ≤ b ≤ 50000),对于第i个每个区间要求有ci个 整数在当前区间内。问:要将每个区间 要求全部满足,至少要选多少个整数。 (a , b , c都是整数类型)
struct edge {
int x, y, nxt; typec c; } bf[E];
void addedge(int x, int y, typec c) {
bf[ne].x = x; bf[ne].y = y; bf[ne].c = c; bf[ne].nxt = head[x]; head[x] = ne++; }
Bellman Ford,复杂度O(V*E),边权 可以为负,不能处理负环。
Spfa ,复杂度O(ke), 其中k为所有顶点 进队的平均次数,边权可为负值,不能 处理负环,当一个点进队超过v次,说明 有负环。
差分约束系统简介
问题模型:线性规划矩阵A的每一行只有 一个1和一个-1,其他元素均为0.由AX ≤解b。给出多个xi-xj≤bk的关系,求向量x可行
dist[p->e] = dist[p->s]+p->v;
if(!visit[p->e]){
visit[p->e] = true;
Q.push(father[p->e]);
}
}
p = p->next;
}
}
单源最短路算法总结
Dijkstra,复杂度O(V^2),边权不能 为负值,可以用堆优化为O(V*logV)
克鲁斯卡尔算法示例代码(b)
void kruskal(int num)
{
for(int i = 0;i < num; ++ i)//这里的边应该从小到大排序过的
{
int s = FindFather(Line[i].s+1);
int e = FindFather(Line[i].e+1);
}
}
if(s != e) {
相关文档
最新文档