为什么写这篇文章?
错误原因分析
因为在题解中,没看到关于正向构图为什么不对的问题的解释。
上题目链接:图的遍历
上我之前认为 的正确代码
#include <iostream>
#include <cstring>
using namespace std;
//稀疏图存储
const int N = 100010;
int h[N], e[N], ne[N], idx;
int n, m;
int d[N]; //记忆化搜索,来存储每个点所能到点的编号最大值。
//建边
void add(int a,int b) {
e[idx] = b, ne[idx] = h[a] ,h[a] = idx++;
}
void dfs(int v) {
if(d[v]) return;
//每个点初始到的编号的最大的点是自己的编号
d[v] = v;
for(int i = h[v]; i != -1; i = ne[i]) {
int j = e[i];
dfs(j);
d[v] = max(d[v], d[j]);
}
}
int main()
{
memset(h, -1, sizeof h);
cin >> n >> m;
while (m -- ) {
int a ,b;
cin >> a >> b;
add(a, b);
}
//这里的小技巧就是先记录最大的点,然后利用记忆化搜索,能达到O(n)的复杂度
for(int i = n;i >= 1; i--)
dfs(i);
for(int i = 1; i <= n; i++) cout << d[i] << " ";
return 0;
}
然后在多方询问下,发现是环的问题,但是有些环是没问题的。
没问题的环如下:
而重点就是有问题的环
样例输入
3 3
2 3
2 1
1 2
这里的3 能到达的最大编号是没问题的,关键在于1和2的d的生成
- 3没有下一条边,就d[3] = 3
- 2有两条边,根据头插法,构建图,导致d[2] = 2,然后就直接进行dfs,遍历到1结点,1进行dfs遍历到2,返回的是2,所有1最后的结果是d[1] = 2
- 然后2得到1返回的结果是2,然后再dfs到3,得到d[2] = 3
- 所以最后的结果是2 3 3
附上运行截图:
总结: 因为有环,导致某点的最大值还未得出,又使得与之相连的点又dfs到了该点,所以其得出的最大值其实不是最大值。
正确代码 还是附上一份吧
思路:反向构图,然后根据直接把最大值传递给能走到它的所有的结点(其实就是从编号大结点的dfs,d[i]重复遍历的不用管,因为肯定是编号小在编号大的遍历到该点之后遍历)
#include <iostream>
#include <cstring>
using namespace std;
//稀疏图存储
const int N = 100010;
int h[N], e[N], ne[N], idx;
int n, m;
int d[N]; //记忆化搜索,来存储每个点所能到点的编号最大值。
//建边
void add(int a,int b) {
e[idx] = b, ne[idx] = h[a] ,h[a] = idx++;
}
void dfs(int v,int k) {
if(d[v]) return;
//每个点初始到的编号的最大的点是自己的编号
d[v] = k;
for(int i = h[v]; i != -1; i = ne[i]) {
int j = e[i];
dfs(j, k);
}
}
int main()
{
memset(h, -1, sizeof h);
cin >> n >> m;
while (m -- ) {
int a ,b;
cin >> a >> b;
add(b , a);
}
//这是先记录最大的点的d值,然后利用记忆化搜索
for(int i = n;i >= 1; i--)
dfs(i ,i);
for(int i = 1; i <= n; i++) cout << d[i] << " ";
return 0;
}