学校网络(含证明)

欧拉回路的证明我都没写,这个我却写了,哎。

题目

题目

做法

第一问其实就是让你求用强连通缩点之后入度为 0 0 0的点。

不难证明的一件事情是,缩点之后是个 D A G DAG DAG,而 D A G DAG DAG必然存在入度为 0 0 0的点(如果不存在,你从一个点出发一直走指向你的边,最后就会走成一个环。)。

入度为 0 0 0的点是肯定需要放的,没什么好说的。但是单单放了入度为 0 0 0的点就行了吗?一个点一直走父亲边就可以到达入度为 0 0 0的点。

第二问的话,设入度为 0 0 0的点集为 P P P,出度为 0 0 0的点集为 Q Q Q,那么答案就是: m a x ( ∣ P ∣ , ∣ Q ∣ ) max(|P|,|Q|) max(P,Q)

惊人的发现我的证法是普通证法的复杂版。

但是为了偷懒直接写普通证法算了

反正至少曾经我想到过证明

参照博客:https://www.acwing.com/solution/content/4663/

首先,先说要实现最小边数的条件。
我们知道一条有向边可以贡献一个入度,一个出度,相应的,也就可以消掉一个 P P P的点和一个 Q Q Q的点,所以至少要 m a x ( ∣ P ∣ , ∣ Q ∣ ) max(|P|,|Q|) max(P,Q)

分类讨论。

  1. ∣ P ∣ = 1 |P|=1 P=1,此时将 Q Q Q中所有的点连向 P P P即可,所用的边的数量为 ∣ Q ∣ |Q| Q
  2. ∣ Q ∣ = 1 |Q|=1 Q=1,此时将 Q Q Q连向 P P P中所有的点即可,所用的边的数量为 ∣ P ∣ |P| P
  3. 其余的情况,我们只需要将 Q Q Q中的一个点 q 1 q_1 q1连向 P P P中的一个点 p 2 p_2 p2,那么这个时候指向 q 1 q_1 q1的的点就会全部指向 p 2 p_2 p2所指向的点,此时刚好把 q 1 q_1 q1 p 2 p_2 p2 P , Q P,Q P,Q中毫无痕迹的删除,然后重复此操作到删除了 m i n ( ∣ P ∣ , ∣ Q ∣ ) − 1 min(|P|,|Q|)-1 min(P,Q)1对点后,开始进入 1 , 2 1,2 1,2操作。

这样的话就证明了一定存在一种方案是 m a x ( ∣ P ∣ , ∣ Q ∣ ) max(|P|,|Q|) max(P,Q)的。

两者一结合,便证明此结论了。

当然,对于入度出度都为 0 0 0的点,你可能会说连一条边只会删掉一个点,但是其实你看这个点啊,拆点,将出去的边和进来的边分开来,这样在对外显示上是没有任何问题的,而且可以帮助你更形象的理解,当然你也可以更硬核的理解,就是 P , Q P,Q P,Q中都有这个点,这个理解在代码时思路会更加清晰,但是结论就摆在那,入度出度都为 0 0 0的点并不会影响结论。
在这里插入图片描述

当然,如果整个图就是一个分量的话,答案是 0 0 0,特判一下即可。

#include<cstdio>
#include<cstring>
#define  N  110
#define  M  110000
using  namespace  std;
inline  int  mymin(int  x,int  y){
    
    return  x<y?x:y;}
inline  int  mymax(int  x,int  y){
    
    return  x>y?x:y;}
struct  node
{
    
    
	int  y,next;
}a[M];int  len,last[N];
inline  void  ins(int  x,int  y){
    
    len++;a[len].y=y;a[len].next=last[x];last[x]=len;}
int  dfn[N],low[N],n,in[N]/*入度*/,out[N]/*出度*/,ti,be[N],sta[N],top,block;
void  dfs(int  x)
{
    
    
	sta[++top]=x;dfn[x]=low[x]=++ti;
	for(int  k=last[x];k;k=a[k].next)
	{
    
    
		int  y=a[k].y;
		if(!dfn[y])
		{
    
    
			dfs(y);
			low[x]=mymin(low[x],low[y]);
		}
		else  if(!be[y])low[x]=mymin(low[x],low[y]);
	}
	if(dfn[x]==low[x])
	{
    
    
		block++;
		while(sta[top]!=x)
		{
    
    
			be[sta[top--]]=block;
		}
		be[sta[top--]]=block;
	}
}
int  main()
{
    
    
	scanf("%d",&n);
	for(int  i=1;i<=n;i++)
	{
    
    
		int  x;
		while(1)
		{
    
    
			scanf("%d",&x);
			if(x==0)break;
			ins(i,x);
		}
	}
	for(int  i=1;i<=n;i++)if(!dfn[i])dfs(i);
	for(int  i=1;i<=n;i++)
	{
    
    
		for(int  k=last[i];k;k=a[k].next)
		{
    
    
			int  y=a[k].y;
			if(be[i]!=be[y])in[be[y]]++,out[be[i]]++;
		}
	}
	int  ans1=0,ans2=0;
	for(int  i=1;i<=block;i++)
	{
    
    
		if(!in[i])ans1++;
		if(!out[i])ans2++;
	}
	if(block==1)printf("1\n0\n");
	else  printf("%d\n%d\n",ans1,mymax(ans1,ans2));
	return  0;
}

猜你喜欢

转载自blog.csdn.net/zhangjianjunab/article/details/108697834