树形DP——树的最大独立集

问题引入

树的最大独立集

对于一棵 n n n个节点的无根树,选出尽量多的节点,使得任何两个节点均不相邻,输出最大独立集的顶点个数

方法一

状态转移

d ( i , k ) d(i,k) d(i,k)表示以 i i i为根节点,选或不选对应的最大独立集大小

  • d ( i , 0 ) d(i,0) d(i,0)表示该节点不选,那么下个节点可以选也可以不选,即 d ( i , 0 ) + = m a x ( d ( s o n [ i ] , 0 ) , d ( s o n [ i ] , 1 ) ) ; {d(i,0)+=max(d(son[i],0),d(son[i],1));} d(i,0)+=max(d(son[i],0),d(son[i],1));
  • d ( i , 1 ) d(i,1) d(i,1)表示该节点被选,那么下个节点一定不能选,即 d ( i , 1 ) + = d ( s o n [ i ] , 0 ) ; {d(i,1)+=d(son[i],0);} d(i,1)+=d(son[i],0);

若根节点为root,那么最终的答案就是 m a x ( d ( r o o t , 0 ) , d ( r o o t , 1 ) ) max(d(root,0),d(root,1)) max(d(root,0),d(root,1)),时间复杂度 O ( n ) O(n) O(n)

PS:不需要将每个节点作为根节点跑一遍,只需要任选一个作为根节点即可得出答案

代码

const int maxn=?;
vector<int> G[maxn];
int d[maxn][2];

int dp(int u,int father,int k){   //k表示决策状态
    d[u][k]=k;
    for(int i=0;i<G[u].size();i++){
        if(G[u][i]!=father){
            if(k) d[u][k]+=dp(G[u][i],u,0);
            else d[u][k]+=max(dp(G[u][i],u,0),dp(G[u][i],u,1));
        }
    }
    return d[u][k];
}
方法二

状态转移

d ( i ) d(i) d(i)表示以 i i i为根节点的子树的最大独立集的大小

节点 i i i有两种决策,选或者不选。如果不选 i i i问题转化为求出 i i i的所有儿子的 d ( s o n [ i ] ) d(son[i]) d(son[i])再相加;否则就是求出 i i i的所有孙子的 d ( g s [ i ] ) d(gs[i]) d(gs[i])再相加,即状态转移方程为:

d ( i ) = m a x ( Σ d ( s o n [ i ] ) , 1 + Σ d ( g s [ i ] ) ) d(i)=max(Σd(son[i]),1+Σd(gs[i])) d(i)=max(Σd(son[i]),1+Σd(gs[i]))

因为儿子和孙子不好求,实际上等价于父亲和祖父,那么我们求出一个 d ( i ) d(i) d(i)后,去更新 i i i的父亲和祖父节点

最终的答案是 m a x ( d ( i ) ) , 1 ≤ i ≤ n max(d(i)),1 \leq i \leq n max(d(i)),1in,时间复杂度 O ( n 2 ) O(n^2) O(n2)

PS:因为每次从根节点开始决策选或不选,那么对应的独立集状态每次只能得出两种,因此我们需要对所有节点求出d

代码

const int maxn=?;
vector<int> G[maxn];
int d[maxn],son[maxn],gs[maxn],f[maxn];


void dfs(int u,int father){
    f[u]=father;
    for(int i=0;i<G[u].size();i++)
        if(G[u][i]!=father)
            dfs(G[u][i],u);
    d[u]=max(son[u],gs[u]+1);
    if(f[u]!=-1){
        son[f[u]]+=d[u];
        if(f[f[u]]!=-1) gs[f[f[u]]]+=d[u];
    }
}

int solve(){
    int ans=0;
    for(int i=1;i<=n;i++){
        memset(son,0,sizeof son);
        memset(gs,0,sizeof gs);
        dfs(i,-1);
        ans=max(ans,d[i]);
    }
    return ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值