1、图的定义

 是一个顶点集合V和一个顶点间关系的集合E组成,记G=(V,E) 
V:顶点的有限非空集合。 
E:顶点间关系的有限集合(边集)。 
存在一个结点v,可能含有多个前驱节点和后继结点。

1顶点(vertex)

上图中黑色的带数字的点就是顶点,表示某个事物或对象。由于图的术语没有标准化,因此,称顶点为点、节点、结点、端点等都是可以的。叫什么无所谓,理解是什么才是关键。

2边(edge)

ACM图的存储(转载自剑紫青天,但是他github挂了

对于ACM图论方面的题目总是免不了首先要建图存图,使用合适的存图方式不但是AC的必要条件,解题事半功倍。

以下主要分析三种常见的存图方式的优缺点以及代码实现

  • 邻接矩阵
  • 邻接表
  • 链式前向星

邻接矩阵

邻接矩阵是三种存图方式中最简单也最为暴力的一种存图方式了。

存图思想

使用一个矩阵来描述一个图,对于矩阵的第i行第j列的值,表示编号为i的顶点到编号为j的顶点的权值。

代码实现

对于邻接矩阵来说,它的代码实现都十分简单,二维数组就可以了。

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
  1. #include <stdio.h>
    #include <string.h>
  2.  
  3. // 最大顶点数
    const int V = 1000;
  4.  
  5. // 邻接矩阵的定义
    // mat[i][j] 表示 顶点`i`到顶点`j`的权值
  6.  
  7. int mat[V][V];
  8.  
  9. // 邻接矩阵的初始化操作
    // 假设权值为零表示没有该边
  10.  
  11. memset(mat, 0, sizeof(mat))
  12.  
  13. // 增加边
    // 新增顶点`i`到顶点`j`的边,权值为`w`
  14.  
  15. mat[i][j] = w;
  16.  
  17. // 删除边
    // 删除顶点`i`到顶点`j`的边
  18.  
  19. mat[i][j] = 0;
  20.  
  21. // 查询边
    // 查询顶点`i`到顶点`j`的边权
  22.  
  23. mat[i][j];

优点

使用邻接矩阵来进行建图存图有以下优点

  • 简单易学

    这个肯定不用多说,哪怕是没学过线性代数的童鞋也很容易理解这样的存图方式。

  • 代码易写,简单好操作

    上面的代码实现已经展示了要定义这个数据结构以及实现初始化,增加删除边等操作有多么的简单。

  • 对已确定的边进行操作,效率高

    确定边(已知两顶点编号)要进行增加或删除边(或者说更改边权)以及查询边权等操作,时间复杂度为$O(1)$。

  • 易处理重边

    你可以随时覆盖掉重边,可以自己实现存储最新的边,权值最大的边或权值最小的边等。
    当然,如果你非要使用邻接矩阵存图还要存重边也不是不可以。

缺点

邻接矩阵存图虽然简单优雅,但是它的一些缺点却几乎是致命的。

  • 过高的空间复杂度

    对于顶点数V,邻接矩阵存图的空间复杂度高达$O(V^2)$顶点数上了一万可以不用考虑这种存图方式了。
    对于稀疏图来说,邻接矩阵存图内存浪费太严重,这也是邻接矩阵存图在ACM题目中十分罕见的根本原因。

  • 对于不确定边的查询效率一般

    比如,我找个编号为1出发的第一条边我还要一条条边判断是否存在(权值是否为0)。

邻接表

邻接表在三种常用的存图方式中属于较为中庸和普遍的存图方式了,缺点不致命,优点不明显。

存图思想

邻接矩阵对于每个顶点使用定长的数组来存储以该点出发的边的情况。第i个数组的第j个值存储的是从顶点i到顶点j的边的权值。
邻接表则是对于每个顶点使用不定长的链表来存储以该点出发的边的情况。因此对于第i个链表的第j个值实际上存储的是从编号为i的顶点出发的第j条边的情况。

一般来说,如果有边权的话,邻接表的链表存储的是一个结构体,这个结构体存储该边的终点以及边权。

下面给个邻接表与邻接矩阵存图的示例比较。


代码实现

在ACM题目中,动态的数据结构一般是不被推荐的,因为动态开辟内存比较消耗时间,且写起来复杂容易出错。
大部分情况我们使用C++STL里的vector作为链表来实现图的邻接表。

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
  1. #include <vector>
  2.  
  3. using namespace std;
  4.  
  5. // 最大顶点数
    const int V = 100000;
  6.  
  7. // vector实现的邻接表的定义
    // 不考虑边权,存储类型为int型
  8.  
  9. vector<int> e[V];
  10.  
  11. // 邻接表的初始化操作
    // 将起点为`i`的边链表全部清空
  12.  
  13. e[i].clear();
  14.  
  15. // 增加边
    // 新增顶点`i`到顶点`j`的边
  16.  
  17. e[i].push_back(j);
  18.  
  19. // 查询边
  20.  
  21. e[i][0]; // 查询以`i`为起点的第一条边`i->e[i][0]`
  22.  
  23. for (int j=0; j<(int)e[i].size(); ++j) {
    if (e[i][j] == k) { // 查询边`i->k`
    // do something.
    }
    }

优点

  • 较为简单易学

    相比邻接矩阵,无非是数组转链表加上存储值的意义不同而已,不需要转太大的弯。

  • 代码易写,不复杂

    代码实现已经演示过了,较简单,不容易写错。

  • 内存利用率较高

    对于顶点数V与边数E空间复杂度为$O(V+E)$。能较好处理稀疏图的存储

  • 对不确定边的操作方便效率也不错

    比如,要遍历从某点出发的所有边,不会像邻接矩阵一样可能会遍历到不存在的边。

缺点

  • 重边不好处理

    判重比较麻烦,还要遍历已有的边,不能直接判断。
    一般情况下使用邻接表存图是会存储重边的,不会做重边的判断。
    所以如果要解决重边的影响一般不在存边的情况下做文章。

  • 对确定边的操作效率不高

    比如对于给定i->j的边要进行查询或修改等操作只有通过遍历这种方式找到了。

链式前向星

链式前向星是前向星的升级版,因为它可以完美代替前向星,所以就跳过前向星的学习,直接学习链式前向星。

存图思想

这种存图方式的数据结构主要是边集数组,顾名思义,图的边是用数组来存储的
当然想要完美表示图结构,光有一个边集数组还不够,还要有一个数组存储指向每一个点的第一条边的“指针”。
而每一条边都需要存储接下来一条边的“指针”,这样就能够像类似邻接表一样方便遍历每一个点的所有边了。

代码实现

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
  1. #include <stdio.h>
    #include <string.h>
  2.  
  3. // 最大顶点数
    const int V = 100000;
    // 最大边数
    const int E = 100000;
  4.  
  5. // 边结构体的定义
    struct Edge {
    int to; // 表示这条边的另外一个顶点
    int next; // 指向下一条边的数组下标,值为-1表示没有下一条边
    };
  6.  
  7. // head[i] 表示顶点`i`的第一条边的数组下标,-1表示顶点`i`没有边
    int head[V];
    Edge edge[E];
  8.  
  9. // 链式前向星初始化,只需要初始化顶点数组就可以了
    memset(head, -1, sizeof(head));
  10.  
  11. // 增加边的方式
    // 新增边 a -> b,该边的数组下标为`id`
    inline void AddEdge(int a, int b, int id)
    {
    edge[id].to = b;
    edge[id].next = head[a]; // 新增的边要成为顶点`a`的第一条边,而不是最后一条边
    head[a] = id;
    return;
    }
  12.  
  13. // 遍历从`a`点出去的所有边
    for (int i=head[a]; i!=-1; i=e[i].next) {
    // e[i] 就是你当前遍历的边 a -> e[i].to
    }

优点

  • 内存利用率高

    相比vector实现的邻接表而言,可以准确开辟最多边数的内存,不像vector实现的邻接表有爆内存的风险。

  • 对不确定边的操作方便效率也不错

    这点和邻接表一样,不会遍历到不存在的边。

缺点

  • 难于理解,代码较复杂

    这种存图方式相对于邻接表来说比较难理解,代码虽然不是很复杂但是不熟练的话写起来也不是方便。

  • 重边不好处理

    这点与邻接表一样,只有通过遍历判重。

  • 对确定边的操作效率不高

    也与邻接表一样,不能通过两点马上确定边,只能遍历查找。

总结

对于邻接矩阵存图来说,由于内存消耗的局限性,它的适用范围比较狭窄,几乎只能在简单图论题目中见到。

邻接表存图最为常见的一种,绝大部分采用C++STL中的vector实现,一般情况下大部分图论题目都能使用该存图方式。

但是链式前向星其实是一种较好替代邻接表来存图的数据结构,在邻接表存图不能使用时可以使用,几乎可以用于全部图论题目。

上图中顶点之间蓝色的线条就是边,表示事物与事物之间的关系。需要注意的是边表示的是顶点之间的逻辑关系,粗细长短都无所谓的。包括上面的顶点也一样,表示逻辑事物或对象,画的时候大小形状都无所谓。

最短路是什么呢,就是两个顶点间最短的距离

floyd算法(3重循环的思行代码

  1. for(int k=; k<=n; k++)
  2. for(int i=; i<=n; i++)
  3. for(int j=; j<=n; j++)
  4. M[i][j]=min(M[i][j],M[i][k]+M[k][j]);

n条边m条路

  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<algorithm>
  4. using namespace std;
  5. const int INF=0x3f3f3f3f;
  6. int main()
  7. {
  8. int n,m,M[][];
  9. scanf("%d%d",&n,&m);
  10. memset(M,INF,sizeof M);
  11. for(int i=; i<=n; i++)
  12. M[i][i]=;
  13. for(int i=,u,v,w; i<=m; i++)
  14. scanf("%d%d%d",&u,&v,&w),M[u][v]=w;
  15. for(int k=; k<=n; k++)
  16. for(int i=; i<=n; i++)
  17. for(int j=; j<=n; j++)
  18. M[i][j]=min(M[i][j],M[i][k]+M[k][j]);
  19. for(int i=; i<=n; i++)
  20. {
  21. for(int j=; j<=n; j++)
  22. printf("%d ",M[i][j]);
  23. printf("\n");
  24. }
  25. return ;
  26. }

题目推荐 Stockbroker Grapevine

附录:矩阵相乘

矩阵A乘以B(15 分)

给定两个矩阵A和B,要求你计算它们的乘积矩阵AB。需要注意的是,只有规模匹配的矩阵才可以相乘。即若A有R​a​​行、C​a​​列,B有R​b​​行、C​b​​列,则只有C​a​​与R​b​​相等时,两个矩阵才能相乘。

输入格式:

输入先后给出两个矩阵A和B。对于每个矩阵,首先在一行中给出其行数R和列数C,随后R行,每行给出C个整数,以1个空格分隔,且行首尾没有多余的空格。输入保证两个矩阵的R和C都是正数,并且所有整数的绝对值不超过100。

输出格式:

若输入的两个矩阵的规模是匹配的,则按照输入的格式输出乘积矩阵AB,否则输出Error: Ca != Rb,其中Ca是A的列数,Rb是B的行数。

输入样例1:

  1. 2 3
  2. 1 2 3
  3. 4 5 6
  4. 3 4
  5. 7 8 9 0
  6. -1 -2 -3 -4
  7. 5 6 7 8

输出样例1:

  1. 2 4
  2. 20 22 24 16
  3. 53 58 63 28

输入样例2:

  1. 3 2
  2. 38 26
  3. 43 -5
  4. 0 17
  5. 3 2
  6. -11 57
  7. 99 68
  8. 81 72

输出样例2:

  1. Error: 2 != 3
作者: 陈越
单位: 浙江大学
时间限制: 400ms
内存限制: 64MB
代码长度限制: 16KB

线代概念题
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int A[105][105],B[105][105],C[105][105];
  4. int main()
  5. {
  6. int a,b;
  7. cin>>a>>b;
  8. for(int i=0; i<a; i++)
  9. for(int j=0; j<b; j++)
  10. cin>>A[i][j];
  11. int c,d;
  12. cin>>c>>d;
  13. for(int i=0; i<c; i++)
  14. for(int j=0; j<d; j++)
  15. cin>>B[i][j];
  16. if(b!=c)
  17. cout<<"Error: "<<b<<" != "<<c;
  18. else
  19. {
  20. cout<<a<<" "<<d<<"\n";
  21. for(int i=0; i<a; i++)
  22. for(int j=0; j<d; j++)
  23. for(int k=0; k<b; k++)
  24. C[i][j]+=A[i][k]*B[k][j];
  25. for(int i=0; i<a; i++)
  26. {
  27. cout<<C[i][0];
  28. for(int j=1; j<d; j++)
  29. cout<<" "<<C[i][j];
  30. cout<<"\n";
  31. }
  32. }
  33. }

acm之图论基础的更多相关文章

  1. java在acm中常用基础技巧方法

    java在acm中常用基础技巧方法 如果学到了新的技巧,本博客会更新~ input input-std @Frosero import java.util.*; public class Main { ...

  2. Two Graphs 牛客网暑期ACM多校训练营(第一场)D 图论基础知识 全排列

    链接:https://www.nowcoder.com/acm/contest/139/D来源:牛客网 Two undirected simple graphs and where are isomo ...

  3. 图论基础之Dijkstra算法的初探

         图论,顾名思义就是有图有论.        图:由点"Vertex"和边"Edge "组成,且图分为有向图和无向图(本文讨论有向图),之前做毕业设计的 ...

  4. MT【90】图论基础知识及相关例题

    此讲适合参加全国联赛二试的同学 介绍图论和我们学习的一般的知识点比如函数一样,首先要介绍一些定义,只是图论里的定义相对较多,这里给出部分在竞赛中常用到的: 就像学函数的时候,学了定义和相关概念后我们要 ...

  5. ACM的一点基础知识

    所摘内容来自于XJTU小学期ACM培训PPT log 默认以2为底 计算机一秒可以看作1e8次 保证数据计算精度及数据所需必要大小 a=1LL*a*a%p//在计算时通过乘以1LL,临时将Int转化为 ...

  6. ACM俱乐部算法基础练习赛(1)

    A: 水题 代码: #include<cstdio> #include<algorithm> using namespace std; ]; int n,m,c; int ma ...

  7. NOIP2018考前抱佛脚——图论基础复习

    目录 存图方式 邻接矩阵存图 邻接表存图 链式前向星 最小生成树 例1 P1536 村村通 题目描述 输入输出格式 输入输出样例 标程 例2 P1546 最短网络 Agri-Net 题目背景 题目描述 ...

  8. (转载)ACM训练计划,先过一遍基础再按此拼搏吧!!!!

    ACM大量习题题库 ACM大量习题题库 现在网上有许多题库,大多是可以在线评测,所以叫做Online Judge.除了USACO是为IOI准备外,其余几乎全部是大学的ACM竞赛题库. USACO ht ...

  9. ACM基础算法入门及题目列表

    对于刚进入大学的计算机类同学来说,算法与程序设计竞赛算是不错的选择,因为我们每天都在解决问题,锻炼着解决问题的能力. 这里以TZOJ题目为例,如果为其他平台题目我会标注出来,同时我的主页也欢迎大家去访 ...

随机推荐

  1. HDU - 5457 Hold Your Hand (Trie + 最小割)

    Hold Your Hand Time Limit: 1500/1000 MS (Java/Others)    Memory Limit: 65535/102400 K (Java/Others)T ...

  2. python3.6.2利用pyinstaller发布EXE

    我的环境是Ubuntu 16.04,系统自带Python2和Python3 安装 pip3 install pyinstaller 发布exe pyinstaller -F helloworld.py ...

  3. xpath定位和css定位对比

    xpath定位和css定位对比   实际项目中使用较多的是xpath定位和css定位.XPath是XML文档中查找结点的语法,换句话就是通过元素的路径来查找这个元素.xpath比较强大,而css选择器 ...

  4. 《队长说得队》【Alpha】Scrum meeting 1

    项目 内容 这个作业属于哪个课程 >>2016级计算机科学与工程学院软件工程(西北师范大学) 这个作业的要求在哪里 >>实验十二 团队作业8:软件测试与ALPHA冲刺 团队名称 ...

  5. 【费用流】 ICPC 2016 China Final J. Mr.Panda and TubeMaster

    表示“必须选”的模型 题目大意 题目分析 一个格子有四种方式看上去很难处理.将横竖两个方向分开考虑,会发现:因为收益只与相邻格子是否连通有关,所以可以将一个格子拆成表示横竖两个方向的,互相独立的点. ...

  6. JAVA 修改环境变量不重启电脑生效方法

     1. 在安装JDK1.6(高版本)时(本机先安装jdk1.6再安装的jdk1.5),自动将java.exe.javaw.exe.javaws.exe三个可执行文件复制到了C:\Windows\Sys ...

  7. 【php】 phpword下载文件问题

    这个问题是在 科锐国际 工作过程中发现的 word文档的名字(有汉字和空格)在windows系统上遍历是查不到文件的,但是在linux系统上市可以的压缩包里面的中文名word文档,如果出现汉字和空格, ...

  8. 进入JVM的世界:《深入理解JVM虚拟机》-- 思维导图

    进入JVM的世界:<深入理解JVM虚拟机>-- 思维导图 之前一直都是零零散散的看了些JVM的知识,心想这样不行啊!于是便抽空看了一下这本神书,阅罢,醍醐灌顶.豁然开朗.真正的是知其然,更 ...

  9. win10安装pytorch——前面有坑,快跳进去鸭

    嗯!花费了不少时间才把pytorch安装成功.主要原因就是: 清华和中科大的Anaconda国内镜像源关闭了 activate.bat 不是内部或外部命令(这个真实奇怪) 1. 安装过程 可以去Ana ...

  10. apply 与 lambda

    Python中的lambda和apply用法  https://blog.csdn.net/anshuai_aw1/article/details/82347016