树的最大独立集
定义:对于一颗n个结点的无根树(没有确定根的树),选出尽量多的结点,使得任意二个结点均不相邻(称为树的最大独立集)。
思路:我们可以任意选一个点作为根结点,然后建树。
用d(i)表示以i为根结点的子树的最大独立集大小。
对于结点i,只有两种选择,选或者不选,若选,则问题转化为求出i的孙子的d值之,若不选则转化为求i的儿子的d值之和。
状态转移方程:d(i) = max(1 + gs[i], s[i]);
代码:
#include<bits/stdc++.h>
using namespace std;
const int mx=1e5+10;
vector<int> v[maxn];
int d[maxn], s[maxn], gs[maxn],n;
int dfs(int u, int pre)
{
for(int i = 0; i < v[u].size(); i++)
{
int m = v[u][i];
if(m != pre)
dfs(m, u);
s[u] += d[m];
if(pre != -1)
gs[pre] += d[m];
}
d[u] = max(s[u], gs[u] + 1);
return d[u];
}
void init()
{
memset(d, 0, sizeof d);
memset(s, 0, sizeof s);
memset(gs, 0, sizeof gs);
for(int i = 0; i < n; i++)
v[i].clear();
}
int main()
{
while(cin >> n)
{
init();
for(int i = 0; i < n-1; i++)
{
int a, b;
cin >> a >> b;;
v[a].push_back(b);
v[b].push_back(a);
}
cout << dfs(i, -1) << endl;
}
return 0;
}
树的重心
定义:树的重心,也称为质心。对于一棵n个结点的无根树,找到一个点(重心),使得把树以该点为根的有根树时,最大子树的结点数最小。换句话说,删除这个点后最大连通块的点(也叫结点)最小。
性质:
1.树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
2.把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
3.把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
如果以1为重心的话,相对于把1删去,分为二堆,{2},{3,4,5,6,7,8,9,10,11},最大子树的结点数为9.
所以我们很容易看出3是重心,{1,2},{4,7,8},{5,9,10},{6,11},最大子树的结点数为3.
还有一个知识点,就是上方子树,顾名思义就是3号结点上方的子树
然后我们怎么求重心呢,其实很简单。找到一个点,其所有的子树中最大的子树节点数最少,跑一遍dfs就好了。
具体看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
const int mx=1e5+10;
const double PI=cos(-1.0);
vector<int>G[mx];
int ans,sum,vis[mx],son[mx],n;
void dfs(int u,int pre)
{
vis[u]=1;
son[u]=0;
int maxx=0;
for(int i=0; i<G[u].size(); i++)
{
int v=G[u][i];
if(v!=pre)
{
dfs(v,u);
son[u]+=son[v]+1;//以u的子树的结点
maxx=max(maxx,son[v]+1);//儿子,子树的结点
}
}
maxx=max(maxx,n-son[u]-1);//上方子树的结点树,总结点n-(以u的子树的结点+1)
if(maxx<ans||(ans==maxx&&sum>u))
{
ans=maxx;
sum=u;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
sum=n;
ans=n*n;
for(int i=0; i<=n; i++)
G[i].clear();
for(int i=1,x,y; i<n; i++)
{
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1,0);
printf("%d %d\n",sum,ans);
}
return 0;
}
树的最长路径(最远点对)
定义:对于一课n个结点的无根树,找到一条最长路径。换句话说,要找到二个点,使得他们的距离最远。
其实就是找以i为根结点,找二个最长的子树。相加就好了
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
vector<int>G[N];
int a[N], b[N];//a 存储到达最远结点的长度,b记录次长度
int ans = 0;
void dfs(int u, int pre)
{
for(int i = 0; i < G[u].size() ;i++)
{
if(G[u][i]==pre)continue;
dfs(G[u][i],u);
if(a[G[u][i]]+1>=a[u])b[u]=a[u],a[u]=a[G[u][i]]+1;
else if(a[G[u][i]]+1>b[u])b[u]=a[G[u][i]]+1;
}
ans = max(ans, a[u]+b[u]);
return ;
}
int main()
{
int n,u,v;
scanf("%d",&n);
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);
printf("%d\n",ans);
}
来源:CSDN
作者:peng1418975997
链接:https://blog.csdn.net/peng1418975997/article/details/103476935