zoukankan      html  css  js  c++  java
  • 学校网络(含证明)

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

    题目

    题目

    做法

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

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

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

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

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

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

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

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

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

    分类讨论。

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

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

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

    当然,对于入度出度都为(0)的点,你可能会说连一条边只会删掉一个点,但是其实你看这个点啊,拆点,将出去的边和进来的边分开来,这样在对外显示上是没有任何问题的,而且可以帮助你更形象的理解,当然你也可以更硬核的理解,就是(P,Q)中都有这个点,这个理解在代码时思路会更加清晰,但是结论就摆在那,入度出度都为(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
    0
    ");
    	else  printf("%d
    %d
    ",ans1,mymax(ans1,ans2));
    	return  0;
    }
    
  • 相关阅读:
    golang版本管理工具gvm
    golang问题101
    服务器部署Jupyter Notebook
    Python 获取MySql某个表所有字段名
    Python自带HTTP文件传输服务
    log4net 配置
    GridView控件RowDataBound事件中获取列字段值的几种途径
    最简单去Button回车事件
    oracle 多条执行语句同时执行
    MERGE Into
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/13702174.html
Copyright © 2011-2022 走看看