题目链接:
https://ac.nowcoder.com/acm/contest/4479/C
思想:树的换根
在一个树中,首先任意选一个点作为根开始深搜,计算每个子树的节点数量,和这个根节点的深度和,之后从邻接点依次转移,取最小值。
树根转换公式:dp[邻接点深度和]=dp[该点深度和]-num[该点为根的子树的节点数量]+(n-num[ ]);
公式来源:一个树的所有儿子节点的深度和可以通过连接他们的边转移。
深刻理解:最小的深度和一定是来自某次深搜中的,求最小,就要让num[ ]最大,也就是儿子节点为根的树节点数量最多,也就是num[ ]-1;转移关系,u为第一次的根,v1,v2…为u的儿子,之后v为根,其最大子树为num[ v ]-1,之后当找出最大的子树时(节点数量最大的子树),与另一半树作比较,(去掉v之后的所有子树找最大的),之后所有节点中取最大子树最小的,就可以求出最小深度和了,因为子树越小(写不下去了!)因为深度和可以通过找树的重心来找,深刻理解的结论就是树的重心,结点深度和最小!!
树的重心:
#include
#include
#include
using namespace std;
#define max(x,y) (x>y?x:y)
const int N=1e6+1;
int n;
vector<int> G[N];
int son[N];//s[v]:表示以结点v为根的子树的结点个数
bool vis[N];
int res=N<<1,tu;
void dfs(int v,int par){
son[v]=1;
int ans=-1;
for(int i=0;i<G[v].size();++i){
int u=G[v][i];
if(par==u) continue;
dfs(u,v);
son[v]+=son[u];
ans=max(ans,son[u]);//找出结点v拥有的所有子树中
//最大子树的结点个数
}
ans=max(ans,n-son[v]);//去掉v后,能产生的最大子树的结点数 :
//要么在v的子树中产生,要么在另一半树中产生(max=n-son[v],
//即:最大等于n减去v子树的结点总数)
if(ans<res) res=ans,tu=v;
}
//计算深度和
void dfsw(int v,int k){
vis[v]=1;
for(int i=0;i<G[v].size();++i){
int u=G[v][i];
if(vis[u]) continue;
res+=k;
dfsw(u,k+1);
}
}
void solve(){
memset(son,0,sizeof(son));
dfs(1,-1);
res=0;
memset(vis,0,sizeof(vis));
dfsw(tu,1);
cout<<res;
}
int main(){
cin>>n;
for(int i=0;i<n-1;++i){
int a,b;
cin>>a>>b;
G[a].push_back(b);
G[b].push_back(a);
}
solve();
return 0;
}
深搜加动态规划维护最优解:
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=1e6+7;
int head[maxn],dep[maxn],p[maxn],vis[maxn],dp[maxn],cnt,n;
struct edge{
int to,next;
}e[maxn<<1];
void add(int x,int y){
e[++cnt]=edge{
y,head[x]},head[x]=cnt;}
int dfs(int x){
vis[x]=1,p[x]=1;
for(int i=head[x];i;i=e[i].next){
int chi=e[i].to;
if(!vis[chi]){
p[x]+=dfs(chi);
dep[x]+=dep[chi]+p[chi];
//dfs由底及上,最底下的结点深度为(dep=0)
//最底层节点的父亲深度为(dep=0)+(p[子结点]=1)=1
//之后逐渐+1
}
}
return p[x];
}
void solve(int x){
vis[x]=1;
for(int i=head[x];i;i=e[i].next){
int chi=e[i].to;
if(!vis[chi]){
dp[chi]=dp[x]-2*p[chi]+n;
solve(chi);
}
}
}
int main(){
int x,y,m=0x3f3f3f3f;
scanf("%d",&n);
for(int i=1; i<n; i++){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs(1);
memset(vis,0,sizeof(vis));
dp[1]=dep[1];
solve(1);
for(int i=1; i<=n; i++){
m=min(m,dp[i]);
}
printf("%d\n",m);
return 0;
}