非递归DFS

同学问了这个问题,当时的思路是记忆化搜索,因为这很像一棵树,计数其叶子节点。递归的记忆化搜索是很容易实现的,只是考虑到N<=10000,也就是说所搜层数会达到10000层,对于之前一直接触Pascal的我有些接受不了,所以考虑非递归的DFS。因为闲麻烦,索性先敲了一个递归的DFS,提交试试,没想到过了。

具体是这样的:

先给每个科目一个编号:政治(0),地理(1),历史(2),综合(3)。

用f[step][obj]表示在第step层中科目obj这课子树的叶子节点数(即从第step层的科目obj开始到第n层结束会有多少种情况),那么如果i是obj的孩子节点(考完科目obj后有可能会考i),那么f[step][obj] = (f[step][obj] + f[step+1][i]) % 7654321,当然,f[step+1][i]是递归时的返回值,最后输出f[1][0]就可以了。

#include<stdio.h>
#include<string.h>


int f[10010][4];
int next[4][4] = {0,0,1,0,0,0,1,1,1,1,0,0,0,1,0,0};
int n;


int DFS(int step,int obj)
{
    if (f[step][obj])   //已经搜过该状态,直接返回
        return f[step][obj];
    if (step == n)    //到达最底层
    {
        f[step][obj] = 1;
        return 1;
    }
    for (int i=0; i<4; i++)
        if (next[obj][i])
            f[step][obj] = (f[step][obj]+DFS(step+1,i)) %7654321;
    return f[step][obj];
}


int main()
{
    scanf("%d",&n);
    printf("%d\n",DFS(1,0));


    return 0;
}

确认了正确性,于是便开始思考非递归的DFS。

非递归的DFS也是DFS嘛,所以就是用一个手动栈来模拟:

首先,用一个结构体类型的栈来保留当前这一层的所有信息,以便从下一层返回该层时继续该层的任务。

其次,递归是一个循环的过程,只是有时循环体的操作起点不同,用while循环来模拟递归,只要从栈中调用就可以了。

再次,对于返回的情况,因为整个过程是在while循环中进行的,所以返回时只要一句continue就可以了,需要注意的是,在返回之前要把所有的信息都返回到上一层。返回上一层的条件有两个:一是到达了搜索的最底层,二是该层搜索全部完成。返回上一层时记得要消除当前这一层的所有影响,不然当再次搜到这一层时会受到干扰。

最后,对于任意一层(除第一层和最后一层),都有可能是从上一层“递归”进来的,也可能是从下一层返回来的,有些变量在不同情况下的处理是不同的(因为是循环模拟递归,都是从循环体的第一句开始运行的,所以要判断是从哪个方向来的)。

考虑清楚了这些,一个简单的非递归DFS基本就可以成形了。

#include<stdio.h>


struct Stack
{
    int i,obj;
    short flag;  
};
Stack Stacks[10010];   //保存每一层信息的栈
int f[10010][4];
int next[4][4] = {0,0,1,0,0,0,1,1,1,1,0,0,0,1,0,0};
int n;


int DFS()
{
    int step = 1,obj = 0;  //当前搜索层的信息
    short in = 0;  //判断该层是从上一层“递归”来的(in == 1),还是从下一层返回来的(in == 0)。


    if (n == 1)
        return 1;
    while (1)
    {
        if (in && f[step][obj])
        {
            f[step-1][Stacks[step-1].obj] = (f[step-1][Stacks[step-1].obj]+f[step][obj]) % 7654321;  //对该层计算结果的利用
            Stacks[step].i = 0; Stacks[step].obj = 0; Stacks[step].flag = 0;  //消除该层所有影响
            obj = Stacks[step-1].obj; step--;  //把信息返回到上一层
            in = 0;
            continue;
        }
        if (step == n)
        {
            f[step][obj] = 1;
            f[step-1][Stacks[step-1].obj] = (f[step-1][Stacks[step-1].obj]+f[step][obj]) % 7654321;
            Stacks[step].i = 0; Stacks[step].obj = 0; Stacks[step].flag = 0;
            obj = Stacks[step-1].obj; step--;
            in = 0;
            continue;
        }


        if (in)
            Stacks[step].flag = 0;    //如果是“递归”来的,那么该层搜索没有完成,初始化位0
        for (int i=Stacks[step].i; i<4; i++)
        {
            if (next[obj][i])
            {
                if (f[step+1][i])
                    f[step][obj] = (f[step][obj] + f[step+1][i]) % 7654321;  //如果已经搜过了,就直接调用计算结果
                else                                                                                        //否则搜索该状态
                {
                    Stacks[step].i = i+1;
                    Stacks[step].obj = obj;
                    if (i == 3)
                        Stacks[step].flag = 1;  //i 已经到达最后一个状态,可以判断该层所搜完成
                    obj = i; step++;
                    in = 1;
                    break;
                }
            }
            if (i == 3)
            {
                Stacks[step].flag = 1;   //i 已经到达最后一个状态,可以判断该层所搜完成(因为最后一个状态未必会满足搜索条件,但仍然要判断是否搜索完成)
            }
        }
        if (Stacks[step].flag)  //如果搜索完成,则返回上一层
        {
            f[step-1][Stacks[step-1].obj] = (f[step-1][Stacks[step-1].obj]+f[step][obj]) % 7654321;
            Stacks[step].i = 0; Stacks[step].obj = 0; Stacks[step].flag = 0;
            obj = Stacks[step-1].obj; step--;
        }
        if (step == 0)
            break;
    }
    return f[1][0];
}


int main()
{
    scanf("%d",&n);
    printf("%d",DFS());


    return 0;
}

 
 

你可能感兴趣的:(非递归DFS)