题目链接:点击打开链接
题目大意:给你一个有向图,其中可能会有一些有向环,最多去掉其中的一条边,使这个有向图成为一个无环图。
思路:现在网上普遍都是一些拓扑排序求解的代码,虽然当时比赛的时候确实可以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;
}