同学问了这个问题,当时的思路是记忆化搜索,因为这很像一棵树,计数其叶子节点。递归的记忆化搜索是很容易实现的,只是考虑到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;
}