树常用算法

存储结构

const int inf=1e9;
const int maxn=10005;
const int maxm=20005;
typedef struct{
	int u,v,next;
}Edge;
Edge edge[maxm];
int head[maxn],cnt=0;//head[maxn]初始值为-1
int n,m;
void addedge(int u,int v){
	edge[cnt].u=u;
	edge[cnt].v=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}

求树的结点个数

//求树中结点个数
int num=0;
void DFS(int u,int pre){
	num+=1;
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].next;
		if(v==pre) continue;
		DFS(v,u);
	}
}

树的重心

基本知识

概念: 树的重心也叫树的质心。找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
性质:

  1. 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个重心,他们的距离和一样。
  2. 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
  3. 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
  4. 一棵树最多有两个重心,且相邻。

求树的重心(无权)

//求树的重心及距离
int minBlance=inf,g=-1,sumLength;
int dfs(int u,int pre){
	int sum=1,maxSub=0;
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v==pre) continue;
		int t=dfs(v,u);
		maxSub=max(maxSub,t);
		sum+=t;
	}
	maxSub=max(maxSub,n-sum);
	if(maxSub<minBlance){
		minBlance=maxSub;
		g=u;
	}
	sumLength=sumLength+sum-1;
	return sum;
}

求树的重心(结点带权)

//求有权树的重心的及距离
int minBlance=inf,g=-1,sumLength;
int dfs(int u,int pre){
	int sum=weight[u],maxSub=0;
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v==pre) continue;
		int t=dfs(v,u);
		maxSub=max(maxSub,t);
		sum+=t;
	}
	maxSub=max(maxSub,total-sum);
	if(maxSub<minBlance){
		minBlance=maxSub;
		g=u;
	}
	sumLength=sumLength+sum-weight[u];
	return sum;
}

完整代码

无权树

/*
* problem:求无权树的重心
* method:dfs
* date:2020/09/07
*/
#include<iostream>
#include<iomanip>
#include<cmath>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<utility>
#include<cmath>
#define ll long long
using namespace std;
const int inf=1e9;
const int maxn=10005;
const int maxm=20005;
typedef struct{
	int u,v,next;
}Edge;
Edge edge[maxm];
int head[maxn],cnt=0;
int n,m;
void addedge(int u,int v){
	edge[cnt].u=u;
	edge[cnt].v=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}
//求树的重心及距离
int minBlance=inf,g=-1,sumLength;
int dfs(int u,int pre){
	int sum=1,maxSub=0;
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v==pre) continue;
		int t=dfs(v,u);
		maxSub=max(maxSub,t);
		sum+=t;
	}
	maxSub=max(maxSub,n-sum);
	if(maxSub<minBlance){
		minBlance=maxSub;
		g=u;
	}
	sumLength=sumLength+sum-1;
	return sum;
}
int main(){
	int i,j,a,b;
	cin>>n>>m;
	memset(head,-1,sizeof(head));
	for(i=0;i<m;i++){
		cin>>a>>b;
		addedge(a,b);
		addedge(b,a);
	}
	dfs(1,-1);//求重心
	sumLength=0;
	dfs(g,-1);//求距离
	cout<<g<<" "<<sumLength<<endl;
	return 0;
}

有权树

/*
* problem:求有权树的重心
* method:dfs
* date:2020/09/07
*/
#include<iostream>
#include<iomanip>
#include<cmath>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<utility>
#include<cmath>
#define ll long long
using namespace std;
const int inf=1e9;
const int maxn=10005;
const int maxm=20005;
typedef struct{
	int u,v,next;
}Edge;
Edge edge[maxm];
int head[maxn],weight[maxn],total=0,cnt=0;
int n,m;
void addedge(int u,int v){
	edge[cnt].u=u;
	edge[cnt].v=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}
//求有权树的重心的及距离
int minBlance=inf,g=-1,sumLength;
int dfs(int u,int pre){
	int sum=weight[u],maxSub=0;
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v==pre) continue;
		int t=dfs(v,u);
		maxSub=max(maxSub,t);
		sum+=t;
	}
	maxSub=max(maxSub,total-sum);
	if(maxSub<minBlance){
		minBlance=maxSub;
		g=u;
	}
	sumLength=sumLength+sum-weight[u];
	return sum;
}
int main(){
	int i,j,a,b;
	cin>>n>>m;
	memset(head,-1,sizeof(head));
	for(i=1;i<=n;i++){
		cin>>weight[i];
		total+=weight[i];
	}
	for(i=0;i<m;i++){
		cin>>a>>b;
		addedge(a,b);
		addedge(b,a);
	}
	dfs(1,-1);//求重心
	sumLength=0;
	dfs(g,-1);//求最短距离和
	cout<<g<<" "<<sumLength<<endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最小生成图论中的一个重要概念,它是指在一个连通图中找到一棵包含所有顶点且边权值之和最小的生成常用的最小生成算法有以下几种: 1. Prim算法: Prim算法是一种贪心算法,从一个起始顶点开始,逐步扩展生成,直到包含所有顶点。具体步骤如下: - 选择一个起始顶点,将其加入生成。 - 从与生成相邻的顶点中选择一个权值最小的边,将其加入生成。 - 重复上述步骤,直到生成包含所有顶点。 2. Kruskal算法: Kruskal算法也是一种贪心算法,它按照边的权值从小到大的顺序逐步选择边,直到生成包含所有顶点。具体步骤如下: - 将图中的所有边按照权值从小到大排序。 - 依次选择权值最小的边,如果该边的两个顶点不在同一个连通分量中,则将其加入生成,并合并这两个连通分量。 - 重复上述步骤,直到生成包含所有顶点。 3. Boruvka算法: Boruvka算法是一种分阶段的贪心算法,它通过多次迭代来逐步构建生成。具体步骤如下: - 初始化每个顶点为一个独立的连通分量。 - 对于每个连通分量,选择一条最小权值的边,将其加入生成,并合并这两个连通分量。 - 重复上述步骤,直到生成包含所有顶点。 以上是最小生成常用算法示例,你可以根据具体的需求选择合适的算法来解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值