Godfather(树形dp,求重心模板)

题目描述

给一棵树,求树的重心

名词解释
删除一个点,使得形成的多棵树中节点数最大的树的节点数最小。

这个点即为树的重心。

输入格式

一行一个整数n,即树的节点个数(n<=5*10^4)

接下来n-1行,每行两个整数,表示树上一条边。

输出格式
如果有多个重心,则按编号从小到大依次输出

样例输入

6
1 2
2 3
2 5
3 4
3 6

样例输出

2 3

提示
卡vector,建议用手写的链式前向星或邻接表

思路:模板直接写即可,两种做法
1.两次扫描法

#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <iomanip>
#include <stdio.h>
#include <string>
#include <queue>
#include <cmath>
#include <stack>
#include <map>

using namespace std;
const int N = 5e4 + 10;
int n, h[N], ne[N<<2], e[N<<2];
int idx;

void add(int x, int y)
{
	e[idx] = y, ne[idx] = h[x], h[x] = idx ++;
	e[idx] = x, ne[idx] = h[y], h[y] = idx ++;
}
int sum[N];
int vis[N];
void dp(int x)
{
	vis[x] = 1;
	sum[x] = 1;
	for(int i = h[x]; ~i; i = ne[i])
	{
		int ver = e[i];
		if(vis[ver] == 0)
		{
			dp(ver);
			sum[x] += sum[ver];
		}
	}
}
int ans[N], minn = 1<<30;
void dfs(int x)
{
	vis[x] = 1;
	int tmp1 = n - sum[x];
	for(int i = h[x]; ~i; i = ne[i])
	{
		int ver = e[i];
		if(vis[ver] == 0)
		{
			tmp1 = max(tmp1, sum[ver]);
			dfs(ver);
		}
	}
	ans[x] = tmp1;
	minn = min(minn, tmp1);
}
int main()
{
	ios::sync_with_stdio(false), cin.tie(0);
	while(cin >> n && n)
	{
		idx = 0;
		memset(h, -1, sizeof h);
		memset(sum, 0, sizeof sum);
		memset(vis, 0, sizeof vis);
		memset(ans, 0, sizeof ans);
		for(int i = 1; i < n; i++)
		{
			int x, y;
			scanf("%d %d", &x, &y);
			add(x, y);
		}
		dp(1);
		memset(vis, 0, sizeof vis);
		dfs(1);
		bool flag = 0;
		for(int i = 1; i <= n; i++)
		{
			if(ans[i] == minn && flag == 0) cout << i, flag = 1;
			else if(ans[i] == minn) cout << " " << i;
		}
		cout << endl;
	}
	return 0;
}

2.直接dp

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=100010;
int head[maxn],next[maxn],To[maxn],cnt;
int son[maxn],sz[maxn];
int q[maxn],tot,n,root;
void add(int u,int v) {
	
	next[++cnt]=head[u];
	head[u]=cnt;
	To[cnt]=v;
}
void dfs(int u,int fa) {
	sz[u]=1;
	for(int i=head[u]; i; i=next[i]) {
		int v=To[i];
		if(v!=fa) {
			dfs(v,u);
			sz[u]+=sz[v];
			son[u]=max(sz[v],son[u]);
		}
	}
	son[u]=max(son[u],n-sz[u]);
	if(son[u]<son[root]) {
		root=u;
		//重置答案 
		tot=0;
		q[++tot]=u;
	} else if(son[u]==son[root])  q[++tot]=u;
}
int main() {
	while(~scanf("%d",&n)) {
		memset(son,0,sizeof(son));
		memset(sz,0,sizeof(sz));
		memset(head,0,sizeof(head));
		int u,v;
		tot=cnt=0;
		for(int i=1; i<n; i++) {
			scanf("%d%d",&u,&v);
			add(u,v);
			add(v,u);
		}
		root=0;
		son[root]=INF;
		dfs(1,0);
		sort(q+1,q+tot+1);
		printf("%d",q[1]);
		for(int i=2; i<=tot; i++)
			printf(" %d",q[i]);
		printf("\n");
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值