树上dp的入门知识

瘦欲@ 提交于 2019-12-11 02:11:47

树的最大独立集

定义:对于一颗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);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!