PTA习题解析——判断DFS序列的合法性

情景需求#

测试数据#

输入样例#

Copy Highlighter-hljs
6 9 0 1 0 2 0 3 2 1 3 2 4 2 1 4 2 5 4 5 4 0 1 4 2 5 3 0 2 5 1 4 3 0 2 3 1 4 5 3 2 5 1 4 0

输出样例#

Copy Highlighter-hljs
Yes Yes No Yes

情景解析#

也就是说,对于顶点而言我们并不关心顶点的顺序,因为这是一个一对多的关系,我们没有手法去强行规定他们的先后序。因此对于图结构的 DFS,可能会产生不只一个的结果序列,例如从 0 号顶点出发,“ 0 1 4 2 5 3 ” 和 “ 0 2 5 1 4 3 ”都可以认为是正确的序列。
从上文所述,我们明白了这道题是不能够直接去 DFS 得到序列进行单模匹配的,也就是说不能用单纯的验证法去证明序列的正误。什么样的思路是可行的?正着做不行那就反着来,用反证法来实现,即我假设给出的序列是正确的,那么我按照你的序列来遍历应当是正确的,如果能够完成遍历那就说明序列正确,若在任何一个地方发生错误以至于无法 DFS,那就说明序列错误。
根据分析,我称之为“定向的 DFS”,即我虽然是按照 DFS 的模式去找这个序列,但是我的操作是受限的——根据给出的顺序遍历。因此我根据往下深度搜索时需要把已知的序列传递下去,为了使得提取边的信息更为方便,我选择邻接矩阵来存储图结构。

注意事项#

由于要模拟 DFS,我们还是要使用递归,由于数据是受限的,因此递归的接口的正确性需要保证。这里给出 2 个卡了我 4 天的关键点:

  1. 由于图可能是非连通图,因此在最外层还需要加一个循环,放置单个路径挖掘完毕后,后续还有一个独立开来的图结构;
  2. 递归 DFS 就免不了回溯,此时必须保证回溯的接口的各个参数都是正确的,否则一旦遇到稠密图需要回溯时,可能就会因为参数的错误访问了不该访问的结点。

模拟验证#

Yes 样例#

假设验证序列 “ 0 1 4 2 5 3 ”,首先我们进入顶点 0:

根据序列,接下来我要去找顶点 1,路径可走:

根据序列,接下来我要去找顶点 4,路径可走:

根据序列,接下来我要去找顶点 2,路径可走:

根据序列,接下来我要去找顶点 5,路径可走:

根据序列,接下来我要去找顶点 3,路径不存在,这是由于顶点 5 没有指向其他顶点导致的。这个时候我们就回溯




我们发现,回溯到 0 号顶点时,有路径通向 3 号顶点,遍历继续。

经检验,该 DFS 序列正确。

No 样例#

假设验证序列 “ 0 2 3 1 4 5 ”,我们跳到第二步:

根据序列,接下来我要去找顶点 3,路径不存在。但是这个时候,顶点 2 是存在向下深度搜索的路径的,分别是“ 2->1 ” 和 “ 2->5 ”,这就说明了序列是错误的。

代码实现#

judgeDFS 函数#

伪代码#

该函数是个递归函数,用于执行所谓的“定向的 DFS”,同时兼具判断合法性的功能。

代码#

Copy Highlighter-hljs
bool judgeDFS(MGraph& g, int dfs[], int idx) { bool flag = true; visited[dfs[idx]] = 1; if (idx == g->n - 1) { return true; } for (int i = idx + 1; i < g->n; i++) { if (visited[dfs[i]] == 0) { if (g->edges[dfs[idx]][dfs[i]] == 1) { flag = judgeDFS(g, dfs, i); } else { for (int j = 0; j < g->n; j++) { if (g->edges[dfs[idx]][j] == 1 && visited[j] == 0) { flag = false; } } } if (flag == false) { break; } } } return flag; }

主函数#

Copy Highlighter-hljs
#include <iostream> using namespace std; //图的邻接矩阵 #define MAXV 101 typedef struct //图的定义 { int edges[MAXV][MAXV]; //邻接矩阵 int n, e; //顶点数,弧数 } *MGraph, GraphNode; //图的邻接矩阵表示类型 int visited[MAXV]; void CreateMGraph(MGraph& g, int n, int e); //建图 bool judgeDFS(MGraph& g, int dfs[], int idx); int main() { MGraph g; int n, e, fre; int j; int dfs[MAXV]; bool flag = true; cin >> n >> e; CreateMGraph(g, n, e); cin >> fre; for (int i = 0; i < fre; i++) { for (j = 0; j < n; j++) { cin >> dfs[j]; visited[j] = 0; } for (j = 0; j < n; j++) { if (visited[dfs[j]] == 0) { flag = judgeDFS(g, dfs, j); } if (flag == false) { break; } } if (flag == true) { cout << "Yes" << endl; } else { cout << "No" << endl; } } return 0; }
posted @   乌漆WhiteMoon  阅读(995)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示
CONTENTS