问题引入
树的最大独立集:
对于一棵 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)),1≤i≤n,时间复杂度 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;
}