旅游
题目描述:
Cwbc和XHRlyb生活在 s 市,这天他们打算一起出去旅游。
旅行地图上有 n 个城市,它们之间通过 n-1 条道路联通。
Cwbc和XHRlyb第一天会在 s 市住宿,并游览与它距离不超过 1 的所有城市,之后的每天会选择一个城市住宿,然后游览与它距离不超过 1 的所有城市。
他们不想住在一个已经浏览过的城市,又想尽可能多的延长旅行时间。
XHRlyb想知道她与Cwbc最多能度过多少天的时光呢?
聪明的你在仔细阅读题目后,一定可以顺利的解决这个问题!
输入描述:
第一行,两个正整数n和s,表示城市个数和第一天住宿的城市s。
接下来n-1行,每行两个整数x和y,表示城市x与城市y之间有一条双向道路。
输出描述:
第一行,一个非负整数表示答案。
示例1
输入
复制
4 1
1 2
2 3
3 4
输出
复制
2
说明
第一天,在1号城市住宿,游览了1、2号城市。
第二天,在3号城市住宿,游览了4号城市,旅行结束。
备注:
1 ≤ n ≤ 500000, 1 ≤ s, x, y ≤ n。
考察:
树形DP
最大独立集就是,点的个数最多的独立集。
树形DP,字面意思,在树结构上的DP,通常根据比较子节点的最优得到父节点的状态或根据父节点预处理后再遍历得到子节点的解。
操作
- 建树、预处理
由于只需要从上向下递归更新子树的父结点,所以并不需要双向建边。
- 带转移方程树上递归
可以用 dp[i][0] ,dp[i][1]… 表示编号为 i 的节点,状态为 0,1… (此状态根据题意有不同意义)时的最优解。
设 f(x,y) 为判断 x,y 中最优解的条件
eg. dp[i][1] += dp[j][0] ;//为1:表示选
dp[i][0] += f ( dp[j][1] , dp[j][0] ) ;//为0表示不选
思路:
树形DP模板,父节点与子节点有关,如果父节点选了,子节点一定不能选,如果父节点没选,子节点可选可不选。由于根s是必须住的,所以答案就是f[s][1]。
这个问题其实是典型的树的最大独立集,与此类似的还有树的最小点覆盖、最小支配集问题
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int n,s,dp[N][2];
vector<int> G[N];
void dfs(int x,int fa){
dp[x][1]=1;
for(int i=0;i<G[x].size();i++){
int y=G[x][i];
if(y==fa) continue;
dfs(y,x);
dp[x][0]+=max(dp[y][0],dp[y][1]);//如果父节点选了,子节点可选可不选
dp[x][1]+=dp[y][0];//如果父节点选了,子节点一定不能选
}
}
int main(){
scanf("%d%d",&n,&s);
for(int i=1;i<=n-1;i++){
int x,y;
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs(s,0);//随便设fa为1,当y==fa时表示为根节点,结束。
cout<<dp[s][1]<<endl;
return 0;
}