声明
此文章仅为作者复习所用,无教学作用。
存图方法
链式前向星--一个O(n + m) 的存图策略,但是要注意读取的时候是反着来的。
1.查找文献 普及-
原题链接:https://www.luogu.com.cn/problem/P5318
题目描述
- 小 K 喜欢翻看洛谷博客获取知识。每篇文章可能会有若干个(也有可能没有)参考文献的链接指向别的博客文章。小 K 求知欲旺盛,如果他看了某篇文章,那么他一定会去看这篇文章的参考文献(如果他之前已经看过这篇参考文献的话就不用再看它了)。
假设洛谷博客里面一共有 n(n≤10^5) 篇文章(编号为 1 到 n)以及 m(m≤10^6) 条参考文献引用关系。目前小 K 已经打开了编号为 1 的一篇文章,请帮助小 K 设计一种方法,使小 K 可以不重复、不遗漏的看完所有他能看到的文章。这边是已经整理好的参考文献关系图,其中,文献 X → Y 表示文章 X 有参考文献 Y。不保证编号为 1 的文章没有被其他文章引用。
请对这个图分别进行 DFS 和 BFS,并输出遍历结果。如果有很多篇文章可以参阅,请先看编号较小的那篇(因此你可能需要先排序)。 - 输入格式:
共 m+1 行,第 1 行为 2 个数,n 和 m,分别表示一共有 n(n≤10^5) 篇文章(编号为 1 到 n)以及m(m≤10^6) 条参考文献引用关系。
接下来 m 行,每行有两个整数 X,Y 表示文章 X 有参考文献 Y。 - 输出格式:共 2 行。 第一行为 DFS 遍历结果,第二行为 BFS 遍历结果。
示例
- 输入:
8 9
1 2
1 3
1 4
2 5
2 6
3 7
4 7
4 8
7 8 - 输出:
1 2 5 6 3 7 8 4
1 2 3 4 5 6 7 8
思路
- 题目要求先看编号小的那一篇,但是链式前向星是后序遍历来的,需要临时使用一个数组存下每一回合访问的编号,并进行排序(从小到大),然后依次存入答案数组中即可。时间复杂度O(m / logn)。
代码
链式前向星
const int N = 1e6 + 10;
int e[N] , ne[N] , h[N] , idx;
bool st[N];
void add(int a , int b)
{
e[idx] = b , ne[idx] = h[a] , h[a] = idx ++;
}
void bfs(int s)
{
vector<int> res;
memset(st , false , sizeof st);
queue<int> q;
q.push(s);
st[s] = true;
res.push_back(s);
while (q.size())
{
vector<int> temp;
int index = q.front();
q.pop();
for (int i = h[index] ; ~i ; i = ne[i])
{
int j = e[i];
if (!st[j])
{
temp.push_back(j);
st[j] = true;
}
}
sort(all(temp));
for (auto i : temp)
{
q.push(i);
res.push_back(i);
}
}
for (auto i : res) cout << i << " ";
}
void dfs(int s)
{
vector<int> temp;
int index = s;
st[index] = true;
cout << index << " ";
for (int i = h[index] ; ~i ; i = ne[i])
temp.push_back(e[i]);
sort(all(temp));
for (auto i : temp) if (!st[i]) dfs(i);
}
void solve()
{
memset(h , -1 , sizeof h);
int n , m;
cin >> n >> m;
for (int i = 1 ; i <= m ; i ++ )
{
int a , b;
cin >> a >> b;
add(a , b);
}
dfs(1);
cout << endl;
bfs(1);
}
2.图的遍历 普及/提高−
原题链接:https://www.luogu.com.cn/problem/P3916
题目描述
- 给出 个点, 条边的有向图,对于每个点 ,求 表示从点 出发,能到达的编号最大的点。
- 输入格式:
第 行 个整数 ,表示点数和边数。
接下来 行,每行 个整数 ,表示边 。点用 编号。 - 输出格式:一行 个整数 。
示例
-
- 输入:
4 3
1 2
2 4
4 3
- 输入:
- 输出:
4 4 3 4
思路
- 最容易想到的是直接从每个点都出发做一次bfs,存下从当前点出发能到达的最大的点。通过率60%。时间复杂度:O(n²)。
- 建立返图。直接从最大的那个点往钱遍历。第一次到的点一定是以当前点为最大的编号的点。如此可以节约很多时间。时间复杂度O(n + m).
代码
链式前向星 60%
const int N = 1e6 + 10;
int e[N] , ne[N] , h[N] , idx;
bool st[N];
void add(int a , int b)
{
e[idx] = b , ne[idx] = h[a] , h[a] = idx ++;
}
void bfs(int s)
{
int ans = 0;
queue<int> q;
q.push(s);
memset(st , false , sizeof st);
while (q.size())
{
int index = q.front();
st[index] = true;
q.pop();
ans = max(ans , index);
for (int i = h[index] ; ~i ; i = ne[i])
{
int j = e[i];
if (!st[j])
{
st[j] = true;
q.push(j);
}
}
}
cout << ans << " ";
}
void solve()
{
memset(h , -1 , sizeof h);
int n , m;
cin >> n >> m;
for (int i = 1 ; i <= m ; i ++ )
{
int a , b;
cin >> a >> b;
add(a , b);
}
for (int i = 1 ; i <= n ; i ++ ) bfs(i);
}
链式前向星 100%
const int N = 1e5 + 10 , M = 2 * N;
int e[M] , ne[M] , h[N] , idx;
int nums[N];
void add(int a , int b)
{
e[idx] = b , ne[idx] = h[a] , h[a] = idx ++;
}
void bfs(int s)
{
queue<int> q;
q.push(s);
while (q.size())
{
int index = q.front();
q.pop();
for (int i = h[index] ; ~i ; i = ne[i])
{
int j = e[i];
if (!nums[j])
q.push(j) , nums[j] = s;
}
}
}
void solve()
{
memset(h , -1 , sizeof h);
int n , m;
cin >> n >> m;
for (int i = 1 ; i <= m ; i ++ )
{
int a , b;
cin >> a >> b;
add(b , a);
}
for (int i = n ; i ; i -- )
{
if (!nums[i]) bfs(i) , nums[i] = i;
}
for (int i = 1 ; i <= n ; i ++ ) cout << nums[i] << " ";
}
Comments NOTHING