AcWing 4310:树的DFS ← vector、auto、邻接表

【题目来源】
https://www.acwing.com/problem/content/description/4313/

【题目描述】
给定一棵 n 个节点的树。
节点的编号为 1∼n,其中 1 号节点为根节点,每个节点的编号都大于其父节点的编号。
现在,你需要回答 q 个询问。
每个询问给定两个整数 ui,ki。
我们希望你用 DFS(深度优先搜索)算法来遍历根节点为 ui 的子树。
我们规定,当遍历(或回溯)到某一节点时,下一个遍历的目标应该是它的未经遍历的子节点中编号最小的那一个子节点。

例如,上图实例中:
如果遍历根节点为 1 号节点的子树,则子树内各节点的遍历顺序为 [1,2,3,5,6,8,7,9,4]。
如果遍历根节点为 3 号节点的子树,则子树内各节点的遍历顺序为 [3,5,6,8,7,9]。
如果遍历根节点为 7 号节点的子树,则子树内各节点的遍历顺序为 [7,9]。
如果遍历根节点为 9 号节点的子树,则子树内各节点的遍历顺序为 [9]。
每个询问就是让你计算采用规定的 DFS 算法来遍历根节点为 ui 的子树时,第 ki 个被遍历到的节点的编号。

【输入格式】
第一行包含两个整数 n,q。
第二行包含 n−1 个整数 p2,p3,...,pn,其中 pi 表示第 i 号节点的父节点的编号。
接下来 q 行,每行包含两个整数 ui,ki,表示一组询问。

【输出格式】
共 q 行,每组询问输出一行一个整数表示第 ki 个被遍历到的节点的编号。
如果第 ki 个被遍历到的节点不存在,则输出 −1。

【数据范围】
前三个测试点满足 2≤n≤20,1≤q≤20。
所有测试点满足 2≤n≤2×10^5,1≤q≤2×10^5,1≤pi<i,1≤ui,ki≤n。

【输入样例】
9 6
1 1 1 3 5 3 5 7
3 1
1 5
3 4
7 3
1 8
1 9

【输出样例】
3
6
8
-1
9
4

【算法分析】

﹣注意本例中结点的编号从1开始,所以才有了代码中的dfs(1)。

﹣本题要注意区分结点的编号树的DFS序列的下标 的区别。

﹣for(auto iter:vec)的用法:C++11标准引入了 auto 类型说明符。它通过变量的初始值或者表达式中参与运算的数据类型来推断变量的类型。例如:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int main(){
	string s;
	cin>>s;
	for(auto t:s){
		cout<<t<<endl;
	}
		
	return 0;
} 

/*
in:
abc123

out:
a
b
c
1
2
3
*/

-对于一棵树的DFS序列而言,每棵子树的DFS序列对应其中的连续一段。
-树可视为没有环的"有向无权图"。故可借鉴利用STL中的vector实现"有向无权图"的邻接表表示存图的代码实现存树。

cpp 复制代码
/* 利用STL中的vector实现有向无权图的邻接表表示 */
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5;
vector<int> v[N];
 
int main() {
	int n,m; //n点数,m边数
	cin>>n>>m;
	int s,t; //边的邻接点序号,从0开始
 
	for(int i=0; i<m; i++) {
		cin>>s>>t;
		v[s].push_back(t);
		// v[t].push_back(s); 与"无向无权图"的邻接表表示相比,就少此行代码
	}
 
	for(int i=0; i<n; i++) {
		cout<<"V"<<i+1<<":";
		for(int j=0; j<v[i].size(); j++) {
			if(j==v[i].size()-1) cout<<v[i][j];
			else cout<<v[i][j]<<"->";
		}
		cout<<endl;
	}
	return 0;
}

以上关于"有向无权图"的内容解析详见:https://blog.csdn.net/hnjzsyjyj/article/details/101233485

【算法代码】

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int maxn=2e5+5;
int val[maxn]; //val[i]存储DFS序列中下标为i的结点在树中的结点编号
int id[maxn]; //id[i]存储结点编号为i的结点在树的DFS序列中的下标  
int cnt[maxn]; //cnt[i]存储以结点编号为i的结点为根的子树中的结点数 
vector<int> g[maxn];

int cur; //树的DFS序列的下标 
void dfs(int x){ //x为树的结点编号1~n 
    val[cur]=x;
    id[x]=cur;
    cur++;
    cnt[x]=1;
    for(auto t:g[x]){
        dfs(t);
        cnt[x]+=cnt[t];
    }
}

int main(){
    int n,m;
    cin>>n>>m;
    for(int i=2;i<=n;i++){
        int t;
        cin>>t;
        g[t].push_back(i);
    }

    dfs(1);

    //for(int i=1;i<=n;i++) sort(g[i].begin(),g[i].end());

    while(m--){
        int u,k;
        cin>>u>>k;
        if(cnt[u]<k) cout<<"-1"<<endl;
        else cout<<val[id[u]+k-1]<<endl;
    }

    return 0;
}


/*
in:
9 6
1 1 1 3 5 3 5 7
3 1
1 5
3 4
7 3
1 8
1 9

out:
3
6
8
-1
9
4
*/

【参考文献】
https://www.acwing.com/solution/content/97544/
https://blog.csdn.net/Apol1o_/article/details/124563889
https://blog.csdn.net/Jacob0824/article/details/123301752
https://www.acwing.com/solution/content/103008/

相关推荐
sweetheart7-73 天前
LeetCode22. 括号生成(2024冬季每日一题 2)
算法·深度优先·力扣·dfs·左右括号匹配
sweetheart7-76 天前
LeetCode78. 子集(2024秋季每日一题 58)
二进制·dfs·枚举·数组·子集
sweetheart7-78 天前
LeetCode17. 电话号码的字母组合(2024秋季每日一题 59)
算法·深度优先·力扣·dfs
银氨溶液12 天前
力扣——113. 路径总和
数据结构·算法·leetcode·职场和发展·dfs·回溯
王老师青少年编程12 天前
CSP/信奥赛C++刷题训练:经典深搜例题(1):洛谷1605 :迷宫
c++·算法·dfs·深搜·csp·信奥赛
丶Darling.13 天前
代码随想录 | Day36 | 动态规划 :整数拆分&不同的二叉搜索树
c++·笔记·学习·算法·动态规划·dfs·记忆化搜索
無爲謂14 天前
入门 | Kafka数据使用vector消费到Loki中使用grafana展示
分布式·kafka·vector·grafana·loki·日志存储·es限制解决办法
C++oj25 天前
csp普及组算法集训--Dfs
c++·学习·数学·算法·dfs
阑梦清川1 个月前
Java--集合(三)之vector&linkedlist&hashset结构
java·vector·集合·linkedlist·hashcode·vectorhashcode
一棵星1 个月前
遍历有向图链路(DFS算法)- 优化版
java·算法·dfs·图搜索算法