Codeforces Round #786 (Div. 3) G. Remove Directed Edges(拓扑排序+DP)

原题链接:G. Remove Directed Edges


题目大意:

给出一个 n n n 个点, m m m 条边的有向无环图。你要进行删除一些边的操作,操作后,要保证每一个点的入度和出度都至少减少了 1 1 1(如果原来点的出入度为 0 0 0 则不用进行删边操作)。

在满足这个条件的前提下,求出一个最大的点集 S S S 满足任意两点都联通(即 u → v u \rightarrow v uv v → u v \rightarrow u vu ,某一点能被某一点去到即可,别和我一样读假了,以为是强连通,双向奔赴 )。

问操作后这个点集最大的大小是多少?


解题思路:

一眼 D A G DAG DAG ,联想到拓扑排序。

题目中存在删边操作,而且要保证每一个点的入度出度都至少减少了 1 1 1 (题目要求不考虑 0 0 0 入度和 0 0 0 出度的点)。

求出一个最大的点集 S S S 满足任意两点都联通,即就是两点间有一条路径可以相连。那么这一题就转化为了求一条满足题意的路径,也就是最长路问题。

先考虑最长路怎么求,由于这一题是 D A G DAG DAG 图,我们可以转化成拓扑图,然后从拓扑序小的转移到大的,一定会得到一条最长路满足题意,在这些路径中找出一条最长的即可。

考虑选点,哪一些点能够被选入集合里呢?

对于某一些只存在一条出度,或者一条入度的点来说,这一条边是一定要被删去的,那么这一个点就不能 走出去 / / /被到达 。如果我们提前把这些点放入我们的路径当中,这一些点就会把这一条路径截断,显然不优。

那么我们考虑在选点的时候,我们当前这个点 u u u 的出度 o u t out out 要至少大于 1 1 1,去到的点 v v v 的入度 i n in in 要大于 1 1 1,这样,我们才能保证 u → v u \rightarrow v uv 这一条路径,在经过题中操作之后不会被删除。

给一个图解释一下:


对于这一张图,有些点入度只有 1 1 1,比如 ( 2 , 5 ) (2,5) (2,5),有些点出度只有 1 1 1,比如 ( 3 , 4 , 5 ) (3,4,5) (3,4,5),把这一些点考虑进去,是不优的,或者说考虑进来,就会把路径截断。因为删除了这些必须删除的边之后,我们的图就会变成这样:

这就是为什么那么我们考虑在选点的时候,保证 u → v u \rightarrow v uv,我们当前这个点 u u u 的出度 o u t out out 要至少大于 1 1 1,去到的点 v v v 的入度 i n in in 要大于 1 1 1 的原因了。

所以在满足条件的情况下,这张图我们只能构造三个 S S S,分别是 ( 1 , 3 ) (1,3) (1,3) ( 2 , 3 ) (2,3) (2,3) ( 2 , 4 ) (2,4) (2,4),其中集合大小最大为 2 2 2,所以答案为 2 2 2

所以我们在拓扑序 d p dp dp 转移时候,当且仅当 u → v u \rightarrow v uv 满足 o u t u > 1 out_u > 1 outu>1 i n v > 1 in_v > 1 inv>1 才能转移。最后统计答案时,在 d p dp dp 数组取一个 m a x max max 即可。


AC代码:

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;

const int N = 2e5 + 10;
vector<int> g[N];//vector存图

void solve()
{
    int n, m; 
    cin >> n >> m;

    vector<i64> dp(n + 1);

    //由于我们要用到in  所以我们多开一个topin用来跑拓扑序
    vector<int> out(n + 1), in(n + 1), topin(n + 1);

    for (int i = 0, x, y; i < m; ++i)
    {
        cin >> x >> y;
        g[x].emplace_back(y);
        ++out[x], ++in[y], ++topin[y];
    }

    //跑拓扑时顺带dp
    queue<int> que;
    for (int i = 1; i <= n; ++i)
    {
        dp[i] = 1;
        if (!in[i]) que.push(i);
    }

    while (que.size())
    {
        auto t = que.front(); que.pop();
        for (auto& x : g[t])
        {
            if (!--topin[x]) que.push(x);
            if (out[t] > 1 && in[x] > 1)//out > 1 且 in > 1 才转移
                dp[x] = max(dp[x], dp[t] + 1);
        }
    }

    i64 ans = 1;
    for (int i = 1; i <= n; ++i)//统计答案
    {
        ans = max(ans, dp[i]);
        g[i].clear();//顺带多测清空
    }
    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int t = 1; //cin >> t;
    while (t--) solve();

    return 0;
}


谢谢观看!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬味的橙汁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值