首页 > 其他分享 >chapter11-图论

chapter11-图论

时间:2024-03-16 17:11:06浏览次数:22  
标签:图论 int graph MAXN 顶点 chapter11 include dis

1.图的存储方式

首先,关于图的存储方式有2种,一种是邻接矩阵,一种是邻接表,而邻接表适用于1个点对到其他所有点的批处理,实际程序中经常使用。

邻接表会给每一个顶点建立一个单链表,即使那个顶点没有度(无向图),or没有任何出度(有向图)。在程序中,我们并不是使用单链表来存储,而是一个向量数组来表示一个图

#include <iostream>
#include <cstdio>
#include <vector>

using namespace std;

const int MAXN = 1000;

vector<int> graph[MAXN]; //给每个顶点都分配一个向量

2.并查集

并查集用于实现不相交集合的查找Find和合并Union两种操作。

  • 查找:确定元素属于哪个集合。这种方法的步骤是:不断向上查找,直到找到它的根结点,之后根据根结点是否相同来判断两个元素是否属于同一集合。

  • 合并:将两个子集合并成同一个集合。这种方法的步骤是:将一棵树作为另外一棵树的子树,从而使得两棵树变成一颗更大的树。

无论是查找,还是合并首先都要找到子集的根结点,这个操作的耗时与树高h有关,所以应尽可能保持较低的树高,为此要在查找和合并操作中加入一点约束和优化。

  • 查找-路径压缩:在查找某个特定结点的根结点的同时,将将除了根结点之外的所有先辈结点直接指向根结点。

  • 合并-矮树合并大树:为此在father[MAXN]数组的基础上,要增设height[MAXN],初始化为0,并且当且仅当两棵树的树高相同时,合并后的集合树高会加1,其他情况树高均不会发生变化,从而提高查找效率。

并查集用于判定是否是连通图非常方便,因为只要是连通图,必定只有一个连通分量,如果多于1个连通分量,那么就不是连通图。

判定是否是连通图
//判定连通图-并查集 2024-03-16
#include <iostream>
#include <cstdio>

using namespace std;

const int MAXN = 1000 + 10;

int father[MAXN];
int height[MAXN];

void Initial(int n) {
    for(int i = 0; i <= n; ++i) {
        father[i] = i; //初始每个顶点都是孤立的集合,自己是自己的根结点
        height[i] = 0;
    }
}
//查找元素a属于哪个集合,返回根结点编号
int Find(int a) {
    if(father[a] != a) {
        father[a] = Find(father[a]);
    }
    return father[a];
}
//合并元素x和元素y所在的两个子集合
void Union(int x, int y) {
    x = Find(x);
    y = Find(y);
    if(x != y) {
        if(height[x] < height[y]) {
            father[x] = y;
        } else if(height[y] < height[x]) {
            father[y] = x;
        } else {
            father[y] = x;
            height[x]++;
        }
    }
}

int main() {
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF) {
        Initial(n);
        while(m--) {
            int x, y;
            cin >> x >> y;
            Union(x, y);
        }
        //连通分量是否只有1个
        int component = 0;
        for(int i = 1; i <= n; ++i) {
            if(father[i] == i) {
                ++component;
            }
        }
        if(component == 1) {
            cout << "YES" << endl;
        } else {
            cout << "NO" << endl;
        }
    }
    return 0;
}

3.最小生成树

即无向连通图中的极小连通子图,可能不只一棵,但最小生成树的边权和的值是一样的。**如何求解一个连通图的最小生成树,最常见的有Kruskal算法和Prim算法。

kruskal.jpg

最小生成树的权值和
//最小生成树-kruskal算法,以边为依据逐步加入一个集合当中-并查集 2024-03-16
#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 100;

struct Edge {
    int from;
    int to;
    int length;
    bool operator<(const Edge &c) const {
        return length < c.length;
    }
};
Edge edge[MAXN * MAXN];
int father[MAXN];
int height[MAXN];

void Initial(int n) {
    for(int i = 1; i <= n; ++i) {
        father[i] = i;
        height[i] = 0;
    }
}

int Find(int x) {
    if(x != father[x]) {
        father[x] = Find(father[x]);
    }
    return father[x];
}

void Union(int x, int y) {
    x = Find(x);
    y = Find(y);
    if(x != y) {
        if(height[x] < height[y]) {
            father[x] = y;
        } else if(height[y] < height[x]) {
            father[y] = x;
        } else {
            father[y] = x;
            height[x]++;
        }
    }
    return;
}

int Kruskal(int n, int edgeNumber) {
    Initial(n);
    sort(edge, edge + edgeNumber);
    int sum = 0;
    for(int i = 0; i < edgeNumber; ++i) {
        int x = Find(edge[i].from);
        int y = Find(edge[i].to);
        if(x != y) {
            Union(x, y);
            sum += edge[i].length;
        }
    }//其实这里还缺一个判定连通分量是否为1,即是否是无向连通图
    return sum;
}

int main() {
    int n;
    while(cin >> n) {
        if(n == 0)
            break;
        int edgeNumber = n * (n -1) / 2;
        for(int i = 0; i < edgeNumber; ++i) {
            scanf("%d%d%d", &edge[i].from, &edge[i].to, &edge[i].length);
        }
        int answer = Kruskal(n, edgeNumber);
        cout << answer << endl;
    }
    return 0;
}

4.最短路径

Dijkstra算法用于解决单源最短路径问题。

Dijkstra算法在运行过程中将顶点集合V分成两个集合S和T。

(1)S:已确定的顶点集合,起始顶点到当前顶点最短的距离已确定。

(2)T=T - S:尚未确定的顶点集合。

初始所有顶点都在T集合中,然后设置起始顶点到各个顶点的距离值,到自己肯定是0,到其他顶点先设置理论上的最大值INF。算法反复从集合T中选择当前到源点s最近的顶点u,将u加入集合S,然后对所有从u发出的边进行松弛操作

松弛操作举例,假设现在把点B加入集合S,对所有从点B发出的边进行松弛操作为:
\(D(u)= min{D(u), D(B) + (B, u)}\)。

单源最短路径
//单源最短路径-用优先队列优化每次选择距离起始顶点最短的顶点 2024-03-16
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>

using namespace std;

const int MAXN = 200;
const int INF = INT_MAX;

struct Edge {
    int to;
    int length;
    Edge(int t, int l): to(t), length(l) {}
};

struct Point {
    int index;
    int distance;
    Point(int i, int d): index(i), distance(d) {}
    bool operator< (const Point &p) const {
        return distance > p.distance;
    }
};

vector<Edge> graph[MAXN];
int dis[MAXN];
priority_queue<Point> myPriortyQueue;

void Dijkstra(int start, int n) {
    fill(dis, dis + MAXN, INF);
    dis[start] = 0;
    myPriortyQueue.push(Point(start, dis[start]));
    while(!myPriortyQueue.empty()) {
        int u = myPriortyQueue.top().index;
        myPriortyQueue.pop();

        for(int i = 0; i < graph[u].size(); ++i) {
            int v = graph[u][i].to;
            int d = graph[u][i].length;
            if(dis[u] + d < dis[v]) {
                dis[v] = dis[u] + d;
                myPriortyQueue.push(Point(v, dis[v])); //if 更新,就放入优先队列中
            }
        }
    }
}

int main() {
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF) {
        memset(graph, 0, sizeof(graph));
        while(m--) {
            int from, to, dis;
            scanf("%d%d%d", &from, &to, &dis);
            graph[from].push_back(Edge(to, dis));
            graph[to].push_back(Edge(from, dis));
        }
        int start, terminal;
        scanf("%d%d", &start, &terminal);
        Dijkstra(start, n);
        if(dis[terminal] == INF) {
            dis[terminal] = -1;
        }
        cout << dis[terminal] << endl;
    }
    return 0;
}

标签:图论,int,graph,MAXN,顶点,chapter11,include,dis
From: https://www.cnblogs.com/paopaotangzu/p/18076895

相关文章

  • 【C++算法模板】图论-拓扑排序,超详细注释带例题
    文章目录0)概述1)Kahn算法1:数据结构2:建图3:Kanh算法2)DFS染色1:数据结构2:建图3:DFS3)算法对比【例题】洛谷B3644推荐视频链接:D01拓扑排序0)概述给定一张有向无环图,排出所有顶点的一个序列A......
  • 图论——倍增LCA 学习笔记
    图论——倍增LCA学习笔记定义最近公共祖先,简称LCA(LowestCommonAncestor)。一个集合\(S\)的最近公共祖先\(\text{LCA}(S)=\text{LCA}(s_1,s_2,\dots,s_k)\)定义为:这个集合中所有节点,其祖先的交集中,离根最远的那个。性质在数值的关系上:\(\text{LCA}(\{u\})=u\);\(\t......
  • 图论:DFS与BFS
    目录1.DFS(图论)1.1.DFS过程1.2.应用2.BFS(图论)2.1.BFS过程2.2.应用2.3.双端队列BFS实现2.4.优先队列BFS(堆优化Dijkstra算法)1.DFS(图论)DFS全称是,中文名是深度优先搜索,是一种用于遍历或搜索树或图的算法。所谓深度优先,就是说每次都尝试向更深的节点走。广义上的DFS:DF......
  • 【计算机算法】【图论】【最优匹配与点云对准问题】最(极)大团算法
    问题团与最大团的定义图顶点集的子集满足任意两个顶点相邻,称该子集是该图的一个团。图的所有团中顶点最多的,即最大的一个或多个,称为图的最大团或极大团。图的最大团的实际应用问题CVPR2023最佳论文之一用最大团算法实现鲁棒的点云对准,有效解决外点问题。顾名思义有矛盾:......
  • 【图论】最小生成树与Prim、Kruskal算法
    求图的最小生成树的Prim、Kruscal算法,其实都是由最小生成树的性质推来的,掌握了该性质,便能较容易地推导出这两种算法。最小生成树的性质无向图G的顶点集为VVV,设......
  • cmd 的图论练习题(近期总结 2024.3.11)
    AGC010ERearranginglink题意:一个序列\(a_{1...n}\),两个人游戏。先手打乱这个序列,然后后手可以多次选择一对相邻的互质的数交换。先手希望最终序列字典序尽量小,后手则相反。两人都绝顶聪明,求最终序列。\(1\len\le2000,\space1\lea_i\le10^8\)考虑不互质的两个数\(a_i,a......
  • 算法随笔——图论:无向图三/四元环计数
    参考:https://oi-wiki.org/graph/rings-count/题目链接:P1989无向图四元环计数求四元环步骤:建双向边。给每条边定向,由度数小的点指向大的,若度数一样则看编号大小。此时只有这几种情况:都可以归类为:枚举起始点A,枚举A<-->B(双向边),枚举B-->C,让C点被访问次数\(cnt\)......
  • dp&图论 杂题选做
    开个新坑qwq。upd:CSP前一周暂时停更。upd:暂时不会更了。P1099经典套路题。算法一:枚举。先dfs求出树的直径,再枚举直径上的每条路径,再dfs一遍求出最小偏心距即可。时间复杂度\(O(n^3)\),足以通过本题(由此可见本题有多水)。算法二:双指针。通过观察可以发现,在固定左端......
  • 【学习笔记】《综述图论中连通性及相关问题的一些处理方法》
    2023集训队论文第一篇。发现好像存在很多我不会/见过但是从来没记住过的结论之类的,所以这篇主要是背结论用的。目录无向图双连通性点双连通分量的性质耳分解割空间与环空间有向图可达性问题强连通性有向环竞赛图记\(u\rightsquigarrowv\)为\(u\)到\(v\)的路径,\(u\t......
  • Pursuit For Artifacts 题解(图论)
    PursuitForArtifacts题解题目给定一张\(n\)个点\(m\)条边的简单无向连通图,边有边权,边权要么为\(0\),要么为\(1\)。每条边只能通过一次(两个方向加起来只能通过一次)。求是否存在一条从\(a\)到\(b\)的路径,满足路径上至少存在一条权为\(1\)的边。\(1\leqn,m\l......