Educational Codeforces Round 36 (Rated for Div. 2)D. Almost Acyclic Graph(正解!!百度的那些拓扑排序现在都已经TLE!)

题目链接:点击打开链接

题目大意:给你一个有向图,其中可能会有一些有向环,最多去掉其中的一条边,使这个有向图成为一个无环图。

思路:现在网上普遍都是一些拓扑排序求解的代码,虽然当时比赛的时候确实可以ac,但更新数据之后将会TLE!!

现在介绍一下Codeforces的官方题解上的方法。

可以概括为一句话:先找出任意的一个有向环,然后对这个有向环上的边进行枚举删除后检查这个有向图是否无环。

为什么可以这么做?

因为如果一个有向图多环图,删除一条边后,变成无环图,那么删除的边是不是一定是这些有向环的公共边。那么意思就是所有的有向环中都包含了那条边,所以我们拿出任意一个有向环,枚举他的边,一定会找到我们所想要的那条边。如果没有,那不好意思,不可能通过删一条边来使这个图变成无环图。

#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define F first
#define S second
#define mp make_pair
#define pll pair<ll, ll>
#define PI acos(-1.0)
#define eps 0.0000001
#define mod 1000000007
#define base 10
using namespace std;
ll n, m, u, v, delu, delv;
vector<ll> g[505];
vector<pll> cycle, hist;
ll clr[505];

bool find_cycle(ll u, ll p) {
    hist.push_back( mp(p, u) );
    if (clr[u] == 1) {
        ll j = hist.size() - 1;
        while(hist[j].F != u) {
            --j;
        }
        for (int i = j; i<= hist.size() - 1; ++i){
            cycle.push_back(hist[i]);
        }
        return true;
    } else if (clr[u] == 2) {
        return false;
    }
    clr[u] = 1;
    for (int i = 0; i<= (ll) g[u].size() - 1; ++i){
        if (find_cycle(g[u][i], u))
            return true;
    }
    clr[u] = 2;
    return false;
}
bool checks(ll u) {
    if (clr[u] == 1) {
        return true;
    } else if (clr[u] == 2) {
        return false;
    }
    clr[u] = 1;
     for (int i = 0; i<= (ll) g[u].size() - 1; ++i){
        if (!(u == delu && g[u][i] == delv) && checks(g[u][i]))
            return true;
    }
    clr[u] = 2;
    return false;
}
int main() {

    cin >> n >> m;
    for (int i = 1; i<= m; ++i){
        cin >> u >> v;
        g[u].push_back(v);
    }
    bool f = false;
    for (int i = 1; i<= n; ++i)//找一个环  并记录这个环的各条边
    {
        if (find_cycle(i, 0)) {
            f = true;
            break;
        }
    }
    if (!f) {
        cout << "YES\n";
    } else {
        for (int i = 0; i<=(ll) cycle.size() - 1; ++i){
            delu = cycle[i].F;
            delv = cycle[i].S;
            for (int j = 1; j<= n; ++j){
                clr[j] = 0;
            }
            bool c = false;
            for (int j = 1; j<= n; ++j) {
                if (checks(j)) {
                    c = true;
                    break;
                }
            }
            if (!c) {
                cout << "YES\n";
                return 0;
            }
        }
        cout << "NO\n";
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值