洛谷P3916图的遍历的正向构造图的问题解释

为什么写这篇文章?

错误原因分析

因为在题解中,没看到关于正向构图为什么不对的问题的解释。
上题目链接:图的遍历
我之前认为 的正确代码

#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的生成

  1. 3没有下一条边,就d[3] = 3
  2. 2有两条边,根据头插法,构建图,导致d[2] = 2,然后就直接进行dfs,遍历到1结点,1进行dfs遍历到2,返回的是2,所有1最后的结果是d[1] = 2
  3. 然后2得到1返回的结果是2,然后再dfs到3,得到d[2] = 3
  4. 所以最后的结果是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;
}
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值