最大子独立集
对于一棵有N个结点的无根树,选出尽量多的结点,使得任何两个结点均不相邻(称为最大独立集)。
输入
第1行:1个整数N(1 <= N <= 6000),表示树的结点个数,树中结点的编号从1..N
接下来N-1行,每行2个整数u,v,表示树中的一条边连接结点u和v
输出
第1行:1个整数,表示最大独立集的结点个数
样例输入
11
1 2
1 3
3 4
3 5
3 6
4 7
4 8
5 9
5 10
6 11
样例输出
7
分析
这一题是学习树形DP的基础,要求掌握,题目本身不难,只是树形DP入门。
由题意可知,当我们选择一个点时,就不能选择与它相邻的。所以这个点分为选与不选两种状态。
所以定义状态Dp[i][1](选择i点的最优解),Dp[i][0](不选i点的最优解)。
最后的解就是Dp[root][1]与Dp[root][0]中大的那个,root是根节点。
可能会问:题目不是说无根树吗?
对,这里是无根树,但由于算法需求,我们先转成有根数,即随便找一个点当根(此后,就一直以它为根)。
在学习树形Dp的阶段,会遇到很多需要无根转有根的。
废话就不多说了,接下来看状态转移方程。
想一想,Dp[i][1]能从什么状态转过来,
这很好想,既然i这个点选了,那它儿子就不选呀。
所以Dp[i][1]+=Dp[v][0]; 其中,v是儿子编号,至于‘+=’嘛,因为答案是叫求和。
那Dp[i][0]呢?
i选了,它儿子必须选吗?
所以Dp[i][0]+=max(Dp[v][0],Dp[v][1]);
下面给出完整代码(注释)
差点忘了,还有输入
题目只是说两边相连,但并没有说谁是父亲。
所以我们用一个动态数组存点的儿子
所以输入点x,y时
就把x的儿子存成y
y的儿子存成x
在求解时,函数传参时维护一个父亲,
只需判断儿子是不是父亲即可。
下面是完整代码
Code
#include<cstdio>
#include<vector>
#include<iostream>
using namespace std;
int n;
int Dp[6005][2];//状态
vector<int>G[6005];//儿子
void Tdp(int x,int fa){
Dp[x][1]=