首页 > 其他分享 >图论基础:欧拉路,拓扑排序,树的直径与重心

图论基础:欧拉路,拓扑排序,树的直径与重心

时间:2022-09-03 11:57:56浏览次数:61  
标签:图论 int 拓扑 head dfs edge 欧拉 直径 dp

欧拉路

首先结论:

一个图存在欧拉路则每个点的入度等于出度或者一个点的入度比出度大一(终点),一个点的入度比出度小一(起点),其他点入度等于出度。

然后是朴实无华的爆算。

void dfs(int x){
    for(int i=head[x];i;i=head[x]){
        head[x]=edge[i].next;
        dfs(edge[i].v);
    }
    s.push(x);
}

当然,我们发现这个递归的深度是\(O(m)\)级别的。所以我们似乎可以模拟系统栈来防止mle的发生。大概就像这样(其实没怎么改)

void euler(int st){
	s.push(st);
	while(!s.empty()){
		int x=s.top(),i=head[x];
		while(i&&v[i])i=edge[i].next;
		if(i){
			s.push(edge[i].v);
			v[i]=v[i^1]=true;
			head[x]=edge[i].next;
		}
		else{
			s.pop();ans.push(x);
		}
	}
}

然后是另一个小小的东西:拓扑排序。(这几个我放在一起纯属因为篇幅太小都不到一百行)

大概是处理DAG图的一种类似bfs的方式,但是有些不同。首先我们建一个栈,统计所有点的入度,并将入度为0的点入栈。然后我们依次对栈中所有点实行操作:

  1. 出栈,扫描所有能连到的点,使其入度-1;
  2. 如果入度为0则入栈。

如此重复,直到栈为空。代码也很简单。(我这个拿vector存的图,懒得改了)

int dgr[114514];
stack<int>s;
void tuopu(){
    for(int i=1;i<=n;i++){
        if(dgr[i]==0)s.push(i);
    }
    while(!s.empty()){
        int v=s.top();s.pop();
        printf("%d ",v);
        for(i=1;i<=in[v][0];i++){
            dgr[in[v][i]]--;
            if(dgr[in[v][i]]==0)s.push(in[v][i]);
        }
    }
}

树的直径。

定义树的直径是树上的最长链(或者它的长度)。

性质:

  1. 直径两端点是叶子。(废话)
  2. 某个距离任意点最远的点一定是直径某端点。(也就是下文两次爆搜求直径的原理)
  3. 对两棵树,若第一棵直径两端点为\(u,v\),第二棵为\(x,y\),两棵树用一条边连起来,那么新树的直径一定是u,v,x,y中两个点。
  4. 对一棵树,若一个点上连一个叶子节点,则最多改变直径一个端点。
  5. 若一棵树存在多条直径,那么它们交于一点且为直径中点。

两种解法:

  1. 树形dp

大概就是统计每个节点子树的最长链和次长链然后加起来统计答案,比较显然。

void dfs(int x,int f){
	dp[x][0]=dp[x][1]=0;//0是当前节点最长的子链 1是次长 
	for(int i=head[x];i;i=edge[i].next){
		if(edge[i].v!=f){
			dfs(edge[i].v,x);
			if(dp[edge[i].v][0]+edge[i].w>dp[x][0]){
				dp[x][1]=dp[x][0];
				dp[x][0]=dp[edge[i].v][0]+edge[i].w;
			}
			else if(dp[edge[i].v][0]+edge[i].w>dp[x][1])dp[x][1]=dp[edge[i].v][0]+edge[i].w;
			ans=max(ans,dp[x][0]+dp[x][1]);
		}
	}
}
  1. 两次爆搜

按照定义,树的直径是树上的最长链。也就是说,我们从任意节点开始搜,距离该节点最远的节点一定是直径的一个端点。然后再爆搜一次就行了。注意这个方法没法处理负权边。

void dfs(int x,int f){
	for(int i=head[x];i;i=edge[i].next){
		if(edge[i].v!=f){
			dis[edge[i].v]=dis[x]+1;
			if(dis[edge[i].v]>dis[zj])zj=edge[i].v;
			dfs(edge[i].v,x);
		}
	}
}
dfs(1,0);
d[zj]=0;dfs(zj,0);
ans=d[zj];

树的重心

定义:对于树上的每一个点,计算其为根时所有子树中最大的子树节点数,值最小的点就是这棵树的重心。

性质:

  1. 只有以重心为根时,所有子树大小都不超过整棵树大小的一半。
  2. 树上所有点到某个点距离和中,到重心距离和最小。如果有多个重心则相同。
  3. 两棵树之间连一条边,新树的重心在原树两重心的链上。
  4. 在一棵树上添加或者删除一个叶子,树的重心最多移动一条边的距离。
  5. 一棵树最多两个重心,且相邻。

求法:简单定义法。统计子树最大值,选最小的。

void find(int x,int fa){
	size[x]=1;maxx[x]=0;
	for(int i=head[x];i;i=edge[i].next){
		if(edge[i].v!=fa){
			find(edge[i].v,x); 
			size[x]+=size[edge[i].v]; 
			maxx[x]=max(maxx[x],size[edge[i].v]);//统计最大子树规模 
		}
	}
	maxx[x]=max(maxx[x],sum-size[x]);//还有自己上边的子树 
	if(maxx[x]<maxx[rt])rt=x;//更新答案 
}
maxx[0]=2147483647;sum=n;

标签:图论,int,拓扑,head,dfs,edge,欧拉,直径,dp
From: https://www.cnblogs.com/gtm1514/p/16652284.html

相关文章

  • 【欧拉回路小记】
    模板条件无向图存在欧拉回路的充要条件是任意一个点的度数都为偶数,且所有的边是联通的(也就是除去孤立点外,图是连通的)有向图存在欧拉回路的充要条件是任意一个点的入度......
  • 欧拉筛素数及积性函数
    欧拉筛素数及积性函数欧拉筛素数intPrime[N],tot;boolNot[N];//true则i不是素数voidGetPrime(constint&n=N-1){Not[1]=true;for(inti=2......
  • 图论
    多源最短路(在曼哈顿图中)(无例题)(使用BFS,队列):操作的地图要有两个特点:既可以表示结果中所要的最短距离,又能记录这个点是否走过,那就全部memset为一个特殊的数-1(这里一定要......
  • 欧拉反演与它的证明
    就是证明,用狄利克雷卷积做,欧拉反演狗都不用/oh\(\sum\limits_{d|n}\varphi(d)=n\)。长得很像狄利克雷卷积,令\(g(n)\)恒等于\(1\),作\(\varphi(n)\)与\(g(n)\)的......
  • 拓扑排序(topsort)
    给定一个n个点m条边的有向图,点的编号是1到n,图中可能存在重边和自环。请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出−1。若一个由图中所有点构成......
  • 一些初赛要用的图论知识
    一些初赛要用的图论知识0x01无向图与连通图n个顶点的无向图有n-1个边(以及以上)被称为连通图,而刚好n-1就是一棵树了0x02简单图的定义没有重边和自环的无向连通图被认......
  • 考研数据结构与算法(七)图论
    @目录一、图的基本概念1.1图的定义1.2基本术语1.2.1有向图1.2.2无向图1.2.3简单图1.2.4多重图1.2.5完全图1.2.6子图1.2.7连通、连通分量、连通图1.2.8强连通1.2.......
  • Highway - 图论 - 树的直径 - 最短路
    http://https://ac.nowcoder.com/acm/problem/52867题目大意有n个城市,城市之间有n-1条无向道路。Bob在任意两个城市之间建造高速公路的花费是这两个城市之间的最短路径......
  • 拓扑排序学习笔记
    前言在学习这篇文章之前,你需要了解的算法有:基本图论知识链式前向星(图的一种存储方式)了解队列、栈等简单数据结构的实现,用STL也行。什么是拓扑排序AOV网的定义......
  • 图论-最短路-迷宫2
    迷宫2题目大意这是一个关于二维格子状迷宫的题目。迷宫的大小为N*M,左上角格子座标为(1,1)、右上角格子座标为(1,M)、左下角格子座标为(N,1)、右下角格子座标为(N,M)。......