图的遍历(邻接矩阵 | 链式前向星 | vector)

题目链接

传送门

先上题目(多多关注_qaq

题目描述

给出 N N N 个点, M M M 条边的有向图,对于每个点 v v v ,求 A ( v ) A(v) A(v) 表示从点 v v v 出发,能到达的编号最大
的点。

输入格式

1 1 1 行, 2 2 2 个整数 N , M N,M N,M

接下来 M M M 行,每行2个整数 U i , V i U_i,V_i Ui,Vi ,表示边 ( U i , V i ) (U_i,V_i) (Ui,Vi) 。点用 1 , 2 , ⋯ , N 1,2,⋯,N 1,2,,N 编号。

输出格式

N N N 个整数 A ( 1 ) , A ( 2 ) , ⋯   , A ( N ) A ( 1 ) , A ( 2 ) , ⋯ , A ( N ) A(1),A(2),\cdots,A(N)A(1),A(2),⋯,A(N) A(1),A(2),,A(N)A(1),A(2),,A(N)

输入

4 3
1 2
2 4
4 3

输出

4 4 3 4

说明/提示

• 对于 60 60 60 % 的数据, 1 ≤ N , M ≤ 1 0 3 1≤N,M≤10^3 1NM103
• 对于 100 100 100 % 的数据, 1 ≤ N , M ≤ 1 0 5 1≤N,M≤10^5 1NM105

本人暴力枚举的时候,自己编译时,发现只有样例过了,自己编的数据一个都没

过(内心有点酸~~~),后来才发现其实是邻接表。

邻接矩阵

先引入一下邻接矩阵吧。!!!不是邻接表

对于一张图~~
在这里插入图片描述

我们可以先弄一张表格,如下:

011
100
100

其中,第i行第j列 表示 第i个城市和第j个城市是否联通(1为是,0为否)

由此,我们可得知 邻接矩阵具有对称性


众所周知 邻接矩阵空间和时间复杂度都是 N 2 N^2 N2

让我们想一个问题:如果n为 1 0 7 10^7 107 呢?,那么将会有大量的空间被浪费。


邻接表

由此,神诞生了——邻接表~~~(此处省略巨多音效 )

尊称:链式前向星

简介:空间和时间复杂度就都是 M M M。对于稀疏图来说, M M M 要远远小于 N 2 N^2 N2

假设有 N N N个点, M M M 条边

3 3
1 2
1 3
2 3

序号headtonext
220
331
030

可以发现,从第 i i i 点开始遍历,就先找到 h e a d [ i ] head[i] head[i] 的序号,再找到 t o [ h e a d [ i ] ] to[head[i]] to[head[i]]

(为点 i i i 可以到达的边的序号,接下来, i i i 就变成 n e x t [ h e a d [ i ] ] next[head[i]] next[head[i]] ,接着循坏

————(本人第一次做 T L E TLE TLE 了无数次_QAQ)

1 1 1 为栗子:先找到 h e a d [ 1 ] head[1] head[1] ,值为 2 2 2 ,接着找到 t o [ 2 ] to[2] to[2] ,值为 3 3 3 ,记录 1 1 1 可以到达 3 3 3 ,再找到 n e x t [ 2 ] next[2] next[2] ,值为 1 1 1 ,然后找到 t o [ 1 ] to[1] to[1] ,值为 2 2 2 ,记录 1 1 1 可以到达 2 2 2 ,最后找到 n e x t [ 1 ] next[1] next[1] ,值为 0 0 0 ,结束遍历。

∴   1 \therefore \ 1  1 可以到达 2 , 3 2,3 2,3

也可以用结构体存储 t o to to n e x t next next 数组

void add(int a, int b)
{ //插入
    p++;
    t[p].next = head[a];
    head[a] = p;
    t[p].to = b;
}

上机程序片段

for(int i = head[x]; i; i = t[i].next)

请查阅
https://blog.csdn.net/vocaloid01/article/details/76576822

https://so.csdn.net/so/search/all?q=%E9%82%BB%E6%8E%A5%E8%A1%A8&t=all&p=1&s=0&tm=0&lv=-1&ft=0&l=&u=

v e c t o r vector vector才是真正的神! ! !

相对于链式前向星, v e c t o r vector vector 则更为简单。

定义

const int maxn = 1e5 + 99;
struct Edge
{
    int to, w;
};
vector<Edge> d[maxn];

初始化

for (int i = 0; i < maxn; i++)
    d[i].clear();

建图

//在点A和点B之间建立一条权值为K的有向边
d[A].push_back({B, k});

遍历

for (int i = 0; i < d[tmp].size(); i++)

主要思路

对于一个点j,只需要把与j链接的点 i i i 感染 ( a n s ) (ans) ans 注意是逆序,就不用判断了——QAQ
如果有强迫症的话,就用 m m a x [ x ] = m a x ( m m a x [ x ] , u ) ; mmax[x]=max(mmax[x],u); mmax[x]=max(mmax[x],u);

接下来就是 插入要反过来 便于感染

add(b,a);

然后从大的点i开始遍历,找到一个点 z z z 开始感染(附值)——而如果有一个比点 i i i 小的点 j j j ,也找到了点 z z z ,那就直接跳过(因为 i > j i>j i>j节约时间

if (mmax[x])
    return;
mmax[x] = u;

在这里插入图片描述

A C   c o d e AC \ code AC code

#include <bits/stdc++.h>
using namespace std;
int n, m, a, b, x, y, c[100001], head[100001], mmax[100001], p;
struct abc
{
    int to, next;
} t[100001];
void dfs(int x, int u)
{ //深搜
    if (mmax[x])
        return;
    mmax[x] = u; //感染  mmax[x]=max(mmax[x],u);
    for (int i = head[x]; i; i = t[i].next)
    {
        dfs(t[i].to, u);
    }
}
void add(int a, int b)
{ //插入
    p++;
    t[p].next = head[a];
    head[a] = p;
    t[p].to = b;
}
int main()
{
    cin >> m >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a >> b;
        add(b, a);
    }
    for (int u = m; u >= 1; u--)
    {              //重点:注意是逆向 后面就不用max()了————啊哈哈~
        dfs(u, u); //第二个u是用来感染的“病毒”
    }
    for (int i = 1; i <= m; i++)
        cout << mmax[i] << " ";
    //cout<<endl<<endl;
    //for(int i=1;i<=m;i++)printf("%3d. %3d %3d %3d\n",i,head[i],t[i].to,t[i].next);
    return 0;
}

T H E   E N D \mathcal{THE \ END} THE END

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值