文章目录
题目链接
先上题目(多多关注_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 1≤N,M≤103
• 对于 100 100 100 % 的数据, 1 ≤ N , M ≤ 1 0 5 1≤N,M≤10^5 1≤N,M≤105
本人暴力枚举的时候,自己编译时,发现只有样例过了,自己编的数据一个都没
过(内心有点酸~~~),后来才发现其实是邻接表。
邻接矩阵
先引入一下邻接矩阵吧。!!!不是邻接表
对于一张图~~
我们可以先弄一张表格,如下:
0 | 1 | 1 |
---|---|---|
1 | 0 | 0 |
1 | 0 | 0 |
其中,第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
序号 | head | to | next |
---|---|---|---|
① | 2 | 2 | 0 |
② | 3 | 3 | 1 |
③ | 0 | 3 | 0 |
可以发现,从第 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