DFS中怎么记录当前“递归结点”的状态量

在写PAT A1111这一题的过程中暴露了自己对DFS的认识还不是很深刻,现在将其中遇到的这个问题记录下来(debug了好久好久呜呜呜~),并对这一类问题做个总结。

首先PAT A111是个很常规的dijkstra+DFS这一类题,在寻找时间最短且结点数最少的路径时,所写的代码如下:

 1 int minNum=inf,num=0;
 2 void DFS_time(int u)
 3 {
 4     num++;
 5     if(u==st)
 6     {
 7         tempPath.push_back(u);
 8         if(num<minNum)
 9         {
10             minNum=num;
11             path_time=tempPath;
12         }
13         tempPath.pop_back();
14         //这里掉了一句
15         //num--;
16         return;
17     }
18 
19     tempPath.push_back(u);
20     for(int i=0;i<pre_time[u].size();i++)
21     {
22         DFS_time(pre_time[u][i]);
23     }
24     tempPath.pop_back();
25     num--;
26 }

  这里用num变量记录当前递归状态下的结点总数,但是问题就在于if(u==st)这个分支的结尾并没有写上num--来还原结点个数,导致遍历过一个叶子结点后,num的数值不再准确,于是后面的程序也不再正确。其实这反映的是一类的问题,就是在递归中如何正确处理记录当前”递归结点“的状态量

  当用全局变量记录结点状态时(例如上面的numtempPath),每到一个新的结点后,状态需要更新,当任意一个结点结束后状态要还原到进入该节点前的状态,上面例子就错在结束叶子结点的访问后并没有将状态复原。

  在排查自己错误的时候也在网上看了一些其他人的代码,发现了另一种记录状态量的方法,觉得可以和用全局变量记录的方式做一个比较,相应的代码如下(原帖地址:https://blog.csdn.net/zhang35/article/details/107729376)

 1 void dfs(vector<vector<int> > &pre, vector<vector<int> > &w, int root, int sum, int &minSum, vector<int> path, vector<int> &minPath){
 2     path.push_back(root);
 3     if (pre[root].size()==0) {  //到达叶子
 4         if (sum < minSum){
 5             minSum = sum;
 6             minPath = path;
 7         }
 8         return;
 9     }
10     for (int i=0; i<pre[root].size(); i++){
11         dfs(pre, w, pre[root][i], sum + w[pre[root][i]][root], minSum, path, minPath);
12     }
13 }
14 
15 //Dijkstra找最短时间路径
16 vector<int> distT(n, MAXINT);
17 vector<vector<int> > preT(n);
18 Dijkstra(src, time, distT, preT);
19 //dfs找最短路径中节点数最小的
20 int minT = MAXINT;
21 vector<int> minPathT;
22 path.clear();
23 vector<vector<int> > nodes(n, vector<int>(n, 1));  //经过节点数量,统一为1
24 dfs(preT, nodes, dst, 0, minT, path, minPathT);

  上面是是直接将状态量作为DFS的一个形参传入,即是dfs函数中的sumpath,从代码上看好像并不需要每次去还原状态量的值,这就显示出这种写法的妙处,全局变量记录状态量时每次都需要去改变它的值,然而dfs搜索的过程中状态时刻都在变换,所以必须一直对它进行修改维护;而以形参的形式记录就好像在每个状态结点上都专门加了一个变量,只需要每次给新的状态变量赋值了,不需要总是去修改一个变量了。

  当然仔细看的话sumpath的更新方式还是有差别的,sum每次到新的状态是就已经更新完成了,path还需要在开头再push_back,但由于每个状态的形参是相互独立的,它们的改变并不会影响到同一层的结点的状态值。

  总结起来说,这种形参的形式可以直接继承或是改变上一层结点的状态量,而同层的结点状态量值只与上一层的状态有关,相互之间不影响,所以不用去还原状态变量值。

 

posted @ 2021-08-26 17:51  Vincent-luo  阅读(46)  评论(0)    收藏  举报