DFS练习一---HDU 1342

参考文章来源:http://blog.csdn.net/pengwill97/article/details/54850852
题目在这里HDU.1342
最近在练习DFS,就找了一些题来做,力求自我总结,有所收获,这个是第一道题目。
首先,深度优先搜索(DFS,Depth-First Search )是搜索手段中的一种,总某个状态开始,不断转移状态直到状态无法转移,然后就退回到上一步的状态,继续转移到其他状态,重复以上过程,直到找到最终的解,所以看上去用递归来写会比教简单。

现在,分析这道题,给出k个(6< k < 13)升的数字,从中找出6个升序的数字,按照顺序输出全部可能,按照DFS来做,一条一条的输出。

先给出代码,如下

#include<stdio.h>
int a[20], b[10], k;
void dfs(int position, int ans)
{
    if (ans == 6)
    {
        for (int i = 0; i < 5; i++)
            printf("%d ", b[i]);
        printf("%d
", b[5]);
        return;
    }
    if (b[0] == a[k - 5])   position = k+1;
    if (position >= k) return;
    b[ans] = a[position];
    dfs(position + 1, ans+1);
    dfs(position + 1, ans);
}
int main()
{
    int flag = 0, i;
    while (scanf("%d", &k) != EOF) {
        if (k == 0)break;
        if (flag != 0) printf("
");
        for (i = 0; i < k; i++)scanf("%d", &a[i]);
        dfs(0, 0);
        flag = 1;
    }
    return 0;
}

main函数分析如下:

int main()
{
    int flag = 0, i;
    while (scanf("%d", &k) != EOF) {
        if (k == 0)break;
        if (flag != 0) printf("
");
        for (i = 0; i < k; i++)scanf("%d", &a[i]);
        dfs(0, 0);
        flag = 1;
    }
    return 0;
}

根据题目要求,输入数据,唯一的要求是最后一组后面没空行,也就是每一组之间有个空行,所以用了个flag来控制,输入0控制退出。
接下来是dfs函数:

void dfs(int position, int ans)
{
    if (ans == 6)
    {
        for (int i = 0; i < 5; i++)
            printf("%d ", b[i]);
        printf("%d
", b[5]);
        return;
    }
    /*if (b[0] == a[k - 5]) position = k + 1;*/
    if (position >= k) return;
    b[ans] = a[position];
    dfs(position + 1, ans);
    dfs(position + 1, ans+1);
}

首先判断是否达到条件边界,即是否满足6个升序数字,答案保存在数组b中,在这里使用传进来的形参之一的ans作为判断标准,看ans是否为6,如果为6,就代表找到了一个可行解,就可以进行一次输出了,输出之后返回上一个递归。
接下来是一个返回语句,如果position大于等于k,就直接返回,这样看来,position是代表当前要搜索的那个数字的数组下标。很显然,如果搜索超过了k,而只给了k个数,只是越界了,所以要返回。
之后,是一个赋值语句,将数组a在position位置的数字保存到数组b的ans位置上。
最后,是两个dfs函数。参数其中一个都是position+1,说明都是将数组a向前推进了一个位置,然后另外一个参数分别是ans和ans+1,即是否选择将数组b的下一个位置的数字包含进来。也就是说,在没有达到边界条件的时候,每次都可以选择将下一个数字加入数组b,或者不加入,这就是两个选择,在接下去的数字里都会决定加入或者不加入然后判断是否满足边界条件。直到输出所有解。

所以,我觉得先是这样:


……
之后,是这样:
第一次结束
这里以其中一个分支为例子,当ans+1等于6,说明已经找到了一个可行解,就可以进行输出了。

分支如下:

这里写图片描述
事实上,当进行到dfs(6,6)的时候,就已经输出了一次,所以没有进入dfs(7,7),返回的时候进入dfs(7,6),然后又进行了一次输出。
这里写图片描述
这里先写了dfs(position+1,ans+1),所以第一条分支就是最左边的这一条,输出之后返回上一个dfs函数,找旁边的一条分支,知道最后一条分支都走完,程序就结束了,也就得到了所有的解。
现在,回过头来,在上面的dfs函数中我注释了一条语句:

if (b[0] == a[k - 5])   position = k+1if (position >= k) return;

这是干嘛的呢?
本来写上面的代码提交后,AC了,时间是15MS,所以我想把的时间它降下来,就有了上面这一句。
当数组b的第一个数字和数组a的第k-5个数字相等的时候,直接将k+1的值赋值给position,实际上,当和下一句和在一起的时候,它就有了跳过某些不合题意的解的能力,也就是剪枝

图如下:

这里写图片描述
在这里,它把从dfs(2,0)之后的分支都给剪了,至于为什么剪这里?
可以看到,数组b的第一个数字和数组a的第k-5个数字的时候,这个时候依然会往下搜索,但是,即使再把之后的所有数字都放入数组b中,那也才5个数字,而题目是需要6个数字,所以在数目都凑不齐的情况下,应该没有再往下搜索的必要了吧。

最后,还有一个问题

虽然题目只是用了DFS算法,但是做这道题的时候我依然交了不少的WA,因为在这之前,我都是先写的dfs(position+1,ans),好像从逻辑上看也没有错,都是加入或者不加入的问题。
但是这样却得不到答案,好一点会得到几组都是7的输出,坏一点,在没有得到答案的情况下就直接退出了。
为什么会得到都是7的输出?
我找了一下原因,我加入前面的剪枝语句,这样一来,当第一次走到dfs(3,0)的时候数组b中包含的数字只有一个就是数组a的最后一个数字,当返回的时候,b中仍然停留这这个数字没有还原,这样就会反复执行position=k+1;
但是如果不加这一句的话,在递归过程中就结束了,程序也没有跑完,也许是栈溢出了。
无奈╮(╯▽╰)。换了个顺序,就过了。Orz

原文地址:https://www.cnblogs.com/FlyerBird/p/9052578.html