[JLOI2009]二叉树问题
题目描述
如下图所示的一棵二叉树的深度、宽度及结点间距离分别为:
- 深度: 4 4 4
- 宽度: 4 4 4
- 结点 8 和 6 之间的距离: 8 8 8
- 结点 7 和 6 之间的距离: 3 3 3
其中宽度表示二叉树上同一层最多的结点个数。
给定一颗以 1 号结点为根的二叉树,请求出其深度、宽度和两个指定节点 x , y x, y x,y 之间的距离。
输入格式
第一行是一个整数,表示树的结点个数
n
n
n。
接下来
n
−
1
n - 1
n−1 行,每行两个整数
u
,
v
u, v
u,v,表示树上存在一条连接
u
,
v
u, v
u,v 的边。
最后一行有两个整数
x
,
y
x, y
x,y,表示求
x
,
y
x, y
x,y 之间的距离。
输出格式
输入三行,每行一个整数,依次表示二叉树的深度、宽度和 x , y x, y x,y 之间的距离。
样例 #1
样例输入 #1
10
1 2
1 3
2 4
2 5
3 6
3 7
5 8
5 9
6 10
8 6
样例输出 #1
4
4
8
提示
对于全部的测试点,保证 1 ≤ u , v , x , y ≤ n ≤ 100 1 \leq u, v, x, y \leq n \leq 100 1≤u,v,x,y≤n≤100,且给出的是一棵树。
题意:
给定一颗 以 1
号结点为根的二叉树,请求出其 深度、宽度和两个指定节点 x, y
之间的距离。
思路:
二叉树深度比较好求,直接 dfs
求解 即可。
对于 二叉树的宽度,在这里提供一个不错的思路:
在宽搜的过程中,我们可以 在队列中添加一个 “0”
标记,作为 宽搜时扩展的层与层之间的区分,同时也是 该层结束的标志(一层的结尾)。
我们举个例子,假设有这样 一棵二叉树:
最开始我们在 第一层 根节点后 添加一个 “0”
标记(第一层只有根),对于 第二层,当遍历到 第一层的 “0”
时,直接在 第二层 结尾处 扩展出一个 “0”
标记 即可。
同样的道理,当遍历到第二层的 “0”
时,直接在第三层 结尾处 扩展出一个 “0”
标记。
每次取得队头 时,如果 对头值为 0
,那么我们 判断一下当前层节点的个数,并 更新最大值 ans
。
最终 ans
即为二叉树的宽度。
代码片段如下:
int bfs(int u) //宽度 返回值即为二叉树宽度
{
int ans = 0;
vector<int> level; //用一个辅助的向量 level 来存储某一层的所有节点,其大小作为更新二叉树宽度 ans 的依据
queue<int> q; q.push(u), q.push(0); //最开始将根节点和 “0” 标记 压入队列
while (q.size())
{
auto t = q.front(); q.pop();
if (!t) //如果 t 是 “0” 标记,说明到了某一层的尽头
{
if (level.empty()) break; //如果当前层结点数为 0,说明二叉树所有节点已经遍历完毕,直接结束遍历
ans = max(ans, (int)level.size()); //否则更新答案(二叉树宽度)
q.push(0); //在下一层结尾处扩展一个 “0” 标记
level.clear(); //清空当前层,准备存下一层
continue;
}
level.push_back(t); //先将当前点加入
if (l[t]) q.push(l[t]); //加左儿子
if (r[t]) q.push(r[t]); //加右儿子
}
return ans;
}
对于给定树中两节点求距离,我们这里直接先用 邻接表存图,之后用 朴素 dijkstra
求解最短路 即可。
注意:
- 在本题中,从二叉树 上方节点往下走每条边权值为
1
,而从下往上走每条边权值为2
,好像题面没有提及。
代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <bits/stdc++.h>
using namespace std;
//#define int long long
//#define map unordered_map
const int N = 110, M = (N << 1), inf = 0x3f3f3f3f;
map<int, int> l, r;
int n;
int depth = -1;
int g[N][N];
int dist[N];
bool st[N];
void dfs1(int u, int dep)//深度
{
if (!l[u] && !r[u])
{
depth = max(depth, dep);
return;
}
if (l[u]) dfs1(l[u], dep + 1);
if (r[u]) dfs1(r[u], dep + 1);
}
int bfs(int u) //宽度 由于本题并没有要求输出某层的节点,因此我们直接同一个 int width 记录当前层节点个数即可,并用 width 更新 ans
{
int ans = 0;
int width = 0; //此处就不用 vector 了,嫌麻烦
queue<int> q; q.push(u), q.push(0);
while (q.size())
{
auto t = q.front(); q.pop();
if (!t)
{
if (!width) break;
ans = max(ans, width);
q.push(0);
width = 0;
continue;
}
width++;
if (l[t]) q.push(l[t]);
if (r[t]) q.push(r[t]);
}
return ans;
}
int dijk(int a, int b)
{
memset(dist, 0x3f, sizeof dist);
memset(st, false, sizeof st);
dist[a] = 0;
for (int i = 0; i < n; ++i)
{
int t = -1;
for (int j = 1; j <= n; ++j)
{
if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j;
}
st[t] = true;
for (int j = 1; j <= n; ++j)
{
dist[j] = min(dist[j], dist[t] + g[t][j]);
}
}
return dist[b];
}
signed main()
{
int T = 1; //cin >> T;
while (T--)
{
memset(g, 0x3f, sizeof g);
cin >> n;
for (int i = 0; i < n - 1; ++i)
{
int x, y; cin >> x >> y;
if (!l[x]) l[x] = y;
else if (!r[x]) r[x] = y;
g[x][y] = min(g[x][y], 1);
g[y][x] = min(g[y][x], 2);
}
dfs1(1, 1);
cout << depth << '\n';
cout << bfs(1) << '\n';
int a, b; cin >> a >> b;
cout << dijk(a, b) << '\n';
}
return 0;
}