和树的最大独立问题类似,先任选一个结点作为根节点,把无根树变成有根树,然后设d(i)表示以i为根的子树的结点的个数。不难发现d(i)=∑d(j)+1,j∈s(i)。s(i)为i结点的所有儿子结点的编号的集合。程序也十分简单:只需要DFS一次,在无根树有根数的同时计算即可,连记忆化都不需要——因为本来就没有重复计算。
代码调试帮助理解:
scanf:
6
1 22 3
2 4
4 5
5 6
printf:2 4
/**********
代码调试
**********/
#include <cstdio>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <cstring>
#include <utility>
#include <string>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
#define eps 1e-10
#define inf 0x3f3f3f3f
#define ll long long
#define CL(a,b) memset(a,b,sizeof(a))
#define MAXN 100010
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int maxn = 5e4 + 5;
vector<int>G[maxn];
int ans, n;
int dp[maxn],tot,num[maxn];
void init(){
ans=INF;
CL(dp,0);
}
void dfs(int u, int fa) { //递归转化以u为根的子树,u的父亲是fa,统计各节点 子节点数目
dp[u]=1;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
cout<<"v="<<v<<" ";
if(v != fa) {
cout<<"dfs"<<v<<","<<u<<" ";
dfs(v, u); //递归转化以v为根的子树
dp[u]+=dp[v];
}
}
}
void dp_dfs(int son,int fa){
int maxx=0;
for(int i = 0; i < G[son].size(); ++i) {
int v = G[son][i];
if(v != fa) {
//cout<<"dfs"<<v<<","<<son<<" ";
dp_dfs(v, son);
maxx=max(maxx,dp[v]);
//cout<<"maxx="<<maxx<<" ";
}
}
//更新答案
maxx = max(n-dp[son], maxx);//n-dp[son]==“上方子树” maxx理解为“自己的子树”
//cout<<"n-dp"<<son<<"="<<n-dp[son]<<" ";
//cout<<"newmaxx="<<maxx<<" ";
if(maxx < ans)
{
tot = 1;
ans = maxx;
num[tot] = son;
}
else if(maxx == ans)
{
tot++;
num[tot] = son;
}
}
int main()
{
int u,v;
while(scanf("%d",&n)==1)
{
init();for(int i=1;i<n;i++) G[i].clear();
for(int i=1; i<n; i++){
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 0);
for(int i=1;i<=n;i++){
cout<<"dp"<<i<<"="<<dp[i]<<" ** ";
};
//cout<<endl;
dp_dfs(1, 0);
sort(num+1, num+tot+1);
printf ("%d",num[1]);
for(int i=2; i<=tot; i++)
printf (" %d",num[i]);
printf ("\n");
}
return 0;
}
代码调试:
更好的代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
int N; // 1<= N <= 20000
const int maxn = 20000;
vector<int> tree[maxn + 5]; // tree[i]表示节点i的相邻节点
int d[maxn + 5]; // d[i]表示以i为根的子树的节点个数
#define INF 10000000
int minNode;
int minBalance;
void dfs(int node, int parent) // node and its parent
{
d[node] = 1; // the node itself
int maxSubTree = 0; // subtree that has the most number of nodes
for (int i = 0; i < tree[node].size(); i++) {
int son = tree[node][i];
if (son != parent) {
dfs(son, node);
d[node] += d[son];
maxSubTree = max(maxSubTree, d[son]);
}
}
maxSubTree = max(maxSubTree, N - d[node]); // "upside substree with (N - d[node]) nodes"
if (maxSubTree < minBalance){
minBalance = maxSubTree;
minNode = node;
}
}
int main()
{
int t;
scanf("%d", &t);
while (t--){
scanf("%d", &N);
for (int i = 1; i <= N - 1; i++){
tree[i].clear();
}
for (int i = 1; i <= N-1; i++){
int u, v;
scanf("%d%d", &u, &v);
tree[u].push_back(v);
tree[v].push_back(u);
}
minNode = 0;
minBalance = INF;
dfs(1, 0); // fist node as root
printf("%d %d\n", minNode, minBalance);
}
return 0;
}
参考资料:百度百科https://baike.baidu.com/item/树的重心/20416316