C - Simple path
题意:
给定一棵有 N 个节点的树和两个节点 x,y ,按顺序输出从节点 x 到节点 y 的路径。
数据范围:
1 ≤ N ≤ 2 ×
1 ≤ X,Y ≤ N
思路1:
根据节点深度和父节点找路径。
先跑一遍DFS求出每个节点的深度后,对于求结点 a,b 的路径,我们需要维护两个数组vector,数组 v1,v2 分别存从节点 a,b 开始的路径;对于结点 a,b,优先深度深的节点往上爬,一直爬到两路径相遇于节点 t,注意这个节点是没有存在任意一个数组中的。
拼接数组中所存的节点,顺序输出数组 v1 ,再输出两路径相遇处的节点 t ,最后倒序输出数组 v2 即可。
Code:
#include<bits/stdc++.h>
#include<math.h>
using namespace std;
#define x first
#define y second
#define int long long
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };
const int N = 200010, INF = 0x3f3f3f3f, mod = 998244353;
typedef pair<int, int>PII;
int n, x, y;
int ne[2*N], h[N], e[2*N], idx;
int dep[N], f[N];
//邻接表存图
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
//dfs求出所有节点的深度
void dfs(int u, int fa)
{
dep[u] = dep[fa] + 1;
f[u]=fa;
for (int i = h[u]; i != -1; i = ne[i]) //遍历节点u的儿子节点
{
int j = e[i];
if (j == fa)continue;
dfs(j, u);
}
}
void solve()
{
cin >> n >> x >> y;
memset(h, -1, sizeof(h));
for (int i = 1; i <= n - 1; i++)
{
int a, b;
cin >> a >> b;
add(a, b), add(b, a); //存边
}
dfs(1, 0); //深搜预处理出树中所有节点的深度
vector<int>v1, v2;
int a = x, b = y;
while (a != b) //根据深度让深度深的节点往上爬,直到两节点的相遇
{
if (dep[a] > dep[b])v1.push_back(a), a = f[a];
else v2.push_back(b), b = f[b];
}
reverse(v2.begin(), v2.end()); //将v2所存的点的顺序逆置
for (auto it : v1)cout << it << " "; //v1中存的是从节点x开始的一部分节点
cout << a << " "; //v1,v2存边时,当遍历到a=b时跳出,则此时的节点a是v1所存点之后的点
for (auto it : v2)cout << it << " "; //输出后半部分所存的点
cout << endl;
}
signed main()
{
//int t;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
思路2:
可以证明,对于树中任意两个不同的顶点 a 和 b,存在一条唯一的从 a 到 b 的简单路径。
直接dfs,把节点 y 当做根节点,从该节点开始dfs预处理所有节点的父节点 fa[],要求的节点x到节点 y 的路径,就是从节点 x 一直向父节点遍历输出直到节点 y 即可。
Code:
#include<bits/stdc++.h>
#include<math.h>
using namespace std;
#define x first
#define y second
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };
const int N = 200010, INF = 0x3f3f3f3f, mod = 998244353;
typedef pair<int, int>PII;
int n, x, y;
vector<int>e[N];
int fa[N];
//dfs预处理所有节点的父节点
void dfs(int u)
{
for(int &v:e[u])
if (v != fa[u])
{
fa[v] = u;
dfs(v);
}
}
void solve()
{
cin >> n >> x >> y;
for (int i = 1; i <= n - 1; i++)
{
int u, v;
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(y); //dfs从节点y开始,预处理所有节点的父节点
for (int u = x; u; u = fa[u]) //dfs的预处理后,从节点x到节点y的路径就可以通过父节点联系在一起
cout << u << " "; //从节点x开始向上走,直到节点y
cout << endl;
}
signed main()
{
IOS;
//int t;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
吐槽:害,有思路但是不会实现的痛苦,虽然不是难题,但是秉承着逢dfs必记录下原则的菜鸟还是写个题解鞭笞下。题目注意点:
1. 好久不写存图的题,有些细节处理忘记了。注意因为存无向图的是同一个边时存两次,所以下标为idx的数组ne[]和e[]初始化范围为2*N,不是N。
2. dfs预处理树中节点的深度,预处理以某一节点为根节点的所有节点的父节点。