PTA习题解析——判断DFS序列的合法性
情景需求
测试数据
输入样例
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
输出样例
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 天的关键点:
- 由于图可能是非连通图,因此在最外层还需要加一个循环,放置单个路径挖掘完毕后,后续还有一个独立开来的图结构;
- 递归 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”,同时兼具判断合法性的功能。
代码
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;
}
主函数
#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;
}