• 图论算法(一)存图与STL第六弹——vector容器


    图论算法(一)存图

    我发现我的博客阅读量贼低,问小伙伴们,ta们都说这些博客太长了QAQ!

    今天来个短亿点的(也短不了多少……)

    进入正题,图论究竟是什么?

    图论就是给你一张图,让你在这张图上进行各种操作,但是我们要进行操作之前,要先明白图是什么。

    如果你还不了解图是什么,我这里稍微BB一两句,如果你已经了解了图是什么,请直接去第二部分!

    Part 1:图是什么

    我们先看一张图片:

    假设有1,2,3,4,5五座城市,

    我们用5个标号的圆圈(1,2,3,4,5)来表示这5个城市。

    其中有一些城市之间有公路,有一些没有(用连线表示)。

    每一条公路也有一定的长度(已经在连线上表出),公路是双向的(这很重要)。

    综合这些元素,我们就得到了上面这个基本的“图”。

    这就是图的基本元素。

    我们用一些术语来表示刚才提到的这些元素。

    城市叫做“点”,图的基本元素就是点和边。

    公路叫做“边”,边分为有向边和无向边,有向边就是只能从A到B但是不能从B到A(你可以理解是A市和B市中有一条A->B的单行线),无向边可以任意来回走。

    公路的长度叫做“权”,代表两个“点”之间的距离。当然,图是抽象的而不是现实世界中存在的,这个距离就可以使负的,我们叫它“负权”。

    如果几个边把几个点连成了一个环形,我们叫它“环”。

    如果一个点有一条边指向了自己,我们叫它“自环”。

    如果一个环的所有构成边的权值和为负,我们就叫他“负环”(我叫他自闭环,因为出现自闭环的图中会导致无限循环,无法算出最短路等……)

    Part 2:怎么存图

    有两种数据结构可以用来存图,而且这两种数据结构都很好想也很简单实现,大概小学生都能想出来。

    第一种,我们叫它“邻接矩阵”。

    所谓邻接矩阵,就是指一个n*n(n是图中点的个数)的int数组。

    比如我这么写:

    #define N 110
    int v[110][110];

    这个数组就可以表示一个有109个点的图,具体我们用v[i][j]表示第i个点到第j个点的边的长度(点i->点i ==0,如果 点i->点j 没有边相连,记为0x3f)。

    邻接矩阵有如下特性:

    1、空间复杂度O(n^2),这里要注意空间会不会爆炸,比如题目中n<=10^5,我们就不能用邻接矩阵。

    2、对于图的指定边的更新操作,复杂度为O(1)。

    具体代码如下:(给出n个点m条边,给出第i条边是x->y权值为z)

    #include<cstdio>
    #include<cstring>
    #define N 1010
    #define IAKIOI 0
    using namespace std;
    int n,m,v[N][N];
    int main()
    {
        memset(v,0x3f,sizeof(v));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            v[x][y]=z;
        }
        for(int i=1;i<=n;i++)
            v[i][i]=0;
        return IAKIOI;
    }

     下面介绍第二种存图的方法——邻接表

    首先我们要知道C++STL库里有一个容器叫做vector,它可以动态地申请空间,这样就节省了邻接矩阵存下“不存在的边”而浪费的宝贵空间。

    下面是邻接表的实现方法

    1 2(1) 3(2)
    2 1(1) ——
    3 2(3) ——

    像这个表格,(标记——的是未使用的内存空间)我们用第一列的数字代表点i,后面跟着的一行表示从点i出发可以到达的点(点没有括号)和边权(边权用括号括起来了)。

    比如第一行的意思就是:点1可以到点2,距离为1,点1可以到点3,距离为2。

    这样我们节省了两个内存空间,当点的数量非常大的时候,节省的空间是非常可观的。

    但是邻接表也有弊端,vector容器调用起来比较慢,我们如果要对一个边进行修改,只能遍历邻接表的某个一维空间,不像邻接矩阵一样可以直接用下标访问。

     Part 3:vector容器

    好了在给出邻接表代码之前我们先康康vector容器是什么东西。

    很简单来说,vector容器就是一个可以任意更改长短的向量,我们可以存下不超过它的长度的数据,当然也可以随时给他增加长度或者减少长度。

    既然是STL模板库里的东西,必定有一些配套的函数,我知道的有以下几个:

    #include<cstdio>
    #include<vector>
    using namespace std;
    vector<int>vec;
    int main()
    {
        int len=vec.size();//返回元素个数 
        vec.push_back(1);//把“1”插入到vector末尾(先增加一个空间)
        vector<int>::iterator it;//定义一个指针it(迭代器)
        it=vec.begin();//迭代器指向第一个元素 
        it=vec.end();//迭代器指向最后一个元素
        for(unsigned int i=0;i<vec.size();i++)//用unsigned,不然会警告错误(不用也没事)
        {
            //……访问每个元素 
        }
        for(vector<int>::iterator it=vec.begin();it!=vec.end();it++)//和上面等价的指针写法
        vec.clear();//清空vector,但不回收内存 
        vector<int>().swap(vec); //清除容器并回收内存 
    }

    上面就是vector的基本用法(其实写邻接表用不了这么多,但是阔以了解一下……)

    下面回到邻接表部分:

    给出代码:

    #include<cstdio>
    #include<vector>
    #define N 10010
    using namespace std;
    struct Node{
        int to,cost;//to表示到达点,cost表示权值 
    };
    int n,m; 
    vector<Node>v[N];//N个vector容器表示N个点的路径情况(看上面的表格就明白) 
    int main()
    {
        scanf("%d%d",&n,&m);//n个点m条边的图 
        for(int i=1;i<=m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            v[x].push_back((Node){y,z});//x点到y点有权值为z的边 
            //v[y].push_back((Node){x,z});无向图要加这句话反向建边 
        }
        return 0;
    }

    以上就是邻接表和邻接矩阵存图的思路和代码,(还有vector的一点点知识,学会了这些就可以开启下一篇的知识——图论啦!)

  • 相关阅读:
    poj 1321 棋盘问题 (dfs)
    poj 3274 Gold Balanced Lineup(哈希 )
    poj 2513 Colored Sticks( 字典树哈希+ 欧拉回路 + 并查集)
    zoj 3351 Bloodsucker(概率 dp)
    poj 1840 Eqs (hash)
    poj 2418 Hardwood Species (map)
    poj 2151 Check the difficulty of problems(概率dp)
    poj 2442 Sequence(优先队列)
    poj 1442 Black Box(堆 优先队列)
    两个STL网址 总结的很好 && c++堆的网址
  • 原文地址:https://www.cnblogs.com/zaza-zt/p/12988885.html
Copyright © 2020-2023  润新知