CF915D Almost Acyclic Graph 拓扑排序

题面

题意:
给n个点,m条边,有向图,求是否能去掉一条边使得原图无环。
\(n<= 500, m <= min(n(n - 1), 10^5)\)

题解

一个朴素的想法:
枚举删哪条边,然后用拓扑排序判断是否还有环。
但是复杂度直接爆炸。
我们考虑删边对我们check过程(拓扑排序)的影响。
删掉一条边,相当于使得它的出点入度减一。
那么不管我们删掉哪条边,只要它的出点一致,那么在拓扑排序看来,我们的操作其实效果一样。
也就是这样会造成大量无用且重复的判断。
所以我们考虑枚举点。
如果一个点有入度,那么我们尝试删除一条到这个点的边,也就是让这个点入度减一,然后用拓扑排序判断一下是否有环。

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 600
#define ac 101000

int n, m;
int in[AC], s[AC];
int q[AC], head, tail;
int Head[AC], date[ac], Next[ac], tot;

inline int read()
{
	int x = 0;char c = getchar();
	while(c > '9' || c < '0')  c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x;
}

inline void add(int f, int w){//加单项边
	date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot, in[w] ++, s[w] ++;
}

void pre()
{
	n = read(), m = read();
	for(R i = 1; i <= m; i ++)
	{
		int x = read(), y = read();
		add(x, y);
	}
}

bool t_sort()
{
	head = 1, tail = 0;
	for(R i = 1; i <= n; i ++) 
		if(!in[i]) q[++ tail] = i;
	while(head <= tail)
	{
		int x = q[head ++];
		for(R i = Head[x]; i; i = Next[i])
		{
			int now = date[i];
			if(!(-- in[now])) q[++ tail] = now;
		}
	}
	return tail == n;
}

void work()
{
	for(R i = 1; i <= n; i ++)
	{
		if(!in[i]) continue;
		-- in[i];
		if(t_sort())
		{
			printf("YES\n");
			return ;
		}
		memcpy(in, s, (n + 1) * 4);
    //    for(R j = 1; j <= n; j ++) in[j] = s[j];	
    }
	printf("NO\n");
}

int main()
{
//	freopen("in.in", "r", stdin);
	pre();
	work();
	return 0;
}
posted @ 2021-10-14 00:06  ww3113306  阅读(52)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。