题目描述
给出一颗n个点n−1条边的树,点的编号为{1,2,…,n-1,n}1,2,…,n−1,n,对于每个点i(1<=i<=n),输出与点{i}i距离为{2}2的点的个数。
两个点的距离定义为两个点最短路径上的边的条数。
输入描述:
第一行一个正整数{n}n。
接下}n−1行每行两个正整数u,v
表示点u,v之间有一条边。
输出描述:
输出共n行,第i行输出一个整数表示与点i距离为2的点的个数。
样例
输入
4
1 2
2 3
3 4
输出
1
1
1
1
题目分析
其实这道题也不是很难,主要考察了大家的套娃(dp)能力,我看大佬说用树形dp,讲实话我觉得很有道理,题目一上来救赎树,这么大的数据量还没发用矩阵暴力乘出来(矩阵暴力乘:是指把边储存在临接矩阵里,矩阵的行数是爹,列数代表儿子,(爹,儿子)这个点为x代表有x条爹指向儿子的有向边,那么矩阵*矩阵得到的矩阵就是所谓的两步可达矩阵了),但是200000的数据量属实恐怖,那么就要另辟蹊径了。
尝试dp
我们在解决任何问题的时候,都要牢记我们解决的是什么,在这个问题里,我们要解决的是”任意一个点i,从他开始两步可达的点的个数”,这让我们马上想到了走楼梯问题,同样是方案数,同样是从另一个点走到另一个点。dp便忽然开朗了。
再重申一遍,我们要求的是“任意一个点i,从他开始两步可达的点的个数“,所以就考虑,任何一个点,怎么走两步到达其他点呢?结论很简单,简单到80岁老奶奶用假牙都能想出来,那就是走一步,再走一步,走两步就行了。所以dp什么也就很明显了,任何一点从他开始两步可达的点的个数=从他开始走一步可达的所有的点,再走一步可以到达点的个数 之和,就是我们要的答案。
(例如一个点x,其一步可达点为a b c,其中a连了3个点(包括x!!!),b连了2个点(包括x!!!),c连了1个点(包括x!!!),那么x的两步可达点的数量=3-1 + 2-1 + 1-1 =3)
AC代码
#include<iostream>
#include<vector>
using namespace std;
vector<int> vr[200100];
int main()
{
int n;
cin>>n;
int u,v;
while(scanf("%d%d",&u,&v)!=EOF)
{
vr[u].push_back(v);
vr[v].push_back(u);
}
for(int i=1;i<=n;i++)
{
int temp;
int ans=0;
for(int j=0;j<vr[i].size();j++)
{
temp=vr[i][j];
ans+=vr[temp].size()-1;
}
printf("%d\n",ans);
}
return 0;
}
接下来我们思考,Dis x
这个问题不难,也很好想到这个问题,如果要是题目中输入了要求x步可达的点的个数,怎么办?
如果x个,那么原函数就要套x层循环,所以就用优雅的暴力——函数递归
我之前写的博客里把他叫做搜索,但是自从用的越来越顺,我开始习惯叫递归,这是一种思想,而不是一种算法,是一种思路,而不是一种套路。当你把算法提炼为思路了,用起来自然会得心应手。
大家有兴趣自己写写吧,我现在这里凌晨1:43了,实在撑不住要去睡觉了,欢迎大家在评论区交流,对我的博客或者对算法竞赛有什么思路,意见都可以私信我,祝大家功力大涨!