dfs

来源:互联网 发布:冒泡排序法c语言 编辑:程序博客网 时间:2024/06/05 12:55

问题一:

问题描述: 输入一个数n,输出1~n的全部排列。


分析

我们可以将这个问题换一个思路来描述,假设有123编号的扑克牌和123编号的箱子,需要将扑克放到箱子里去,一共有多少种放的方法呢?

第一个箱子有3种,第二个剩下2种,最后一个1种,如此得到一种排列;那么,取出最后一个盒子的牌,退到倒数第二个箱子,此时我们有2种选择,去掉刚才已经选择过的,如此又得到了第二种排列;再退到倒数第三个箱子。。。这个过程有明显的探底性质,对吧。

如何将扑克放到箱子里面呢?

for (int i = 1; i <= n; i++) {    box[step] = i;    // step 表示我们正在第step箱子这里}

而我们手上的扑克有的已经被选择了,有的还没有,因此,用一个book数组来标识是否还有这样的扑克

for (int i = 1; i <= n; i++){    if (book[i] == 0) {        box[step] = i;        book[i] = 1;    }}

而处理完第step次之后,就应该处理第step+1次的箱子位置了吧?我们可以用递归的思想来处理吧?

void dfs(int step){    for (int i = 1; i <= n; i++)    {        if (book[i] == 0) {            book[i] = 1;            box[step] = i;     //本次选择第i个扑克,放到step箱子中            dfs(step + 1);     //递归,到第step+1次            book[i] = 0;       //dfs完成上一次选择之后,要退回上一次的选择        }    }}

那么算法运行到什么时候终止本次选择呢?
也就是说我们应该为递归算法确定一个终止条件!
容易想到,箱子这里应该是step运行到n+1的时候,也就是已经排列好了1~n箱子的扑克!

对上述稍做修改:

void dfs(int step){    if (step == n + 1) {        // 打印1~n的排列        for (int i = 1; i <= n; i ++) {            print box[i];        }        return ;    //返回此次选择 !    }    for (int i = 1; i <= n; i++)    {        if (book[i] == 0) {            book[i] = 1;            box[step] = i;     //本次选择第i个扑克,放到step箱子中            dfs(step + 1);     //递归,到第step+1次            book[i] = 0;       //dfs完成上一次选择之后,要退回上一次的选择        }    }}

实现:
思路是正确的,写出来:

#include <stdio.h>#include <stdlib.h>#define MAX 50int n;int box[MAX];int book[MAX];void dfs(int step){        if (step == n + 1) {                // one arrange                 for (int i = 1; i <= n; i++)                        printf("%d ", box[i]);                printf("\n");                return ;        }        for (int i = 1; i <= n; i++) {                if (book[i] == 0)  {                        book[i] = 1;                        box[step] = i;                        dfs(step + 1);  // dfs                        book[i] = 0;    //reuse                }        }}int main(){        scanf("%d", &n);        dfs(1);        getchar();        return 0;}

问题1


理解dfs的关键,在于知道当前该做什么。
之后再进入下一步。
可以总结下dfs的基本模型:

void dfs(int step) {        //判断边界        //尝试每一种可能 for        {            //继续下一步             dfs(step + 1)        }        //返回      }

问题二:

利用上述思想,解一个相似的问题。

现在有1~9的数字,需要找出满足下列条件的数字组合,如何求解?
ABC + DEF = GHI


分析:
把数字当成盒子,然后用dfs做尝试,到达底部(step = 9 到达最后一个盒子)之后,输出满足条件的等式即可。

代码:

#include <iostream>#include <cstdlib>#define MAX 20int box[MAX];   //number in the boxint number[MAX];    //number whether use or not?static int count = 0;bool meet(){    if (100 * box[1] + 10 * box[2] + box[3] + 100 * box[4] + 10 * box[5] + box[6] ==             100 * box[7] + 10 * box[8] + box[9])        return true;    else        return false;}void dfs(const int step){    if (step == 10) {        if (meet() == true) {            std::cout<<box[1]<<box[2]<<box[3]<<" + "<<box[4]<<box[5]<<box[6]<<" = "                <<box[7]<<box[8]<<box[9]<<"   count = "<<++count<<std::endl;        }        return ;    }    for (int i = 1; i <= 9; i++) {        if (number[i] == 0) {            box[step] = i;            number[i] = 1;      //use this number            dfs(step + 1);      //recursive            number[i] = 0;      //这里一定要将刚才选择的取消,才能够进行下一个循环进行下一步尝试        }    }}int main(){    std::cout<<"meet number : "<<std::endl;    dfs(1);     //begin    getchar();    return 0;}

问题三:

有一个 n * m 规格的迷宫(<= 50),单元格要么是空地(0),要么是障碍物(1)
目的:找到一条从起点到终点的最短路径。

输入:
先输入迷宫的大小 n * m
接着输入迷宫,0 代表空地,1代表障碍
最后输入起点和终点的坐标

输出最短路径的距离。

example:
5 4
0 0 1 0
0 0 0 0
0 0 1 0
0 1 0 0
0 0 0 1
1 1 4 3

输出:
7


分析 :
用一个二维数组存迷宫,开始的时候我们在起点(1, 1),需要找到到终点(p, q)的最短路径。
在每个位置处,我们有四个方向可以走:上下左右。约定行走的顺序可以是:右下左上。

用dfs来分析,在当前位置(x, y)处,往可以走的方向进行探索,不能走的时候返回;
直到找到目的地为止(终止条件),然后更新我们的最短距离。

这里为了方便,我们定义一个方向数组:

int next[4][2] = {      {0, 1},     //right    {1, 0},     //down    {0, -1},    //left    {-1, 0}     //up};

通过它来计算下一步的坐标很方便:

for (k = 0; k < 4; k++){    tx = x + next[k][0];    ty = y + next[k][1];}

接下来就要对(tx, ty)进行判断了;
包括:
1. 是否越界
2. 是否是障碍物
3. 这个点是否已经在路径之中?(用一个标识数组book[][] 来记录是否已经走过它)

如果这个点符合上述所有要求,那么就可以进一步扩展它。
dfs(tx, ty, step + 1);

实现:

for (k = 0; k < 4; k++){    tx = x + next[k][0];    ty = y + next[k][1];    //判断是否越界    if (tx<1 || tx>n || ty<1 || ty>m)        continue;    //判断该点是否为障碍物或者已经在路径之中    if (a[tx][ty]==0 && book[tx][ty]==0) {        book[tx][ty] = 1;     //标记这个点已经走过        dfs(tx, ty, step + 1);        book[tx][ty] = 0;     //尝试结束,取消这个点的标记     }}

来看看源码实现:

#include <stdio.h>#include <stdlib.h>#define MAX 50int book[MAX][MAX];     //tagint A[MAX + 1][MAX + 1];    // graph/* *  order : * *  right down left up * */int next[4][2] = {      {0, 1},     //right    {1, 0},     //down    {0, -1},    //left    {-1, 0}     //up};//matrixint N, M;// target int final_x;int final_y;// countstatic int min = 999;void dfs(int x, int y, int step){    if (x == final_x && y == final_y) {        //printf("reach..\n");        if (step < min)             min = step;        return ;    }    for (int k = 0; k < 4; k++) {        int tx = x + next[k][0];        int ty = y + next[k][1];        if (tx > N || tx == 0 || ty > M || ty == 0)            continue;        if (book[tx][ty] == 0 && A[tx][ty] == 0) {            book[tx][ty] = 1;            dfs(tx, ty, step + 1);            book[tx][ty] = 0;         }    }}int main(){    scanf("%d %d", &N, &M);    if (N <= 0 || M <= 0 || N > MAX || M > MAX) {        printf("N or M is out of range!\n");        return -1;    }    for (int i = 1; i <= N; i++)        for (int j = 1; j <= M; j++) {            scanf("%d", &A[i][j]);        }    int start_x, start_y;    scanf("%d %d %d %d", &start_x, &start_y, &final_x, &final_y);    if (final_x <=0 || final_x > MAX || final_y <= 0 || final_y > MAX) {        printf("final_x or final_y is out of range.");        return -1;    }    if (start_x <= 0 || start_x > MAX || start_y <= 0 || start_y > MAX) {        printf("start_x or start_y is out of range.") ;        return -1;    }    book[start_x][start_y] = 1;     //already use the start point.    dfs(start_x, start_y, 0);    printf("result of min is : %d\n", min);    return 0;}

问题四:

来看一个地图问题

问题4

我们有城市地图的数据,现在需要从1号点行走到5号点,如何去的路程最短呢?

数据是这样的:

5 81 2 21 5 102 3 32 5 73 1 43 4 44 5 55 3 3

第一行的输入表示5个城市,8代表边的条数
接下来的数据a b c 分别代表城市编号以及城市之间的距离数。

现在要求出起点(1)到终点(5)的最短路径?


分析:

可以用dfs也可以用bfs来实现。

先考虑dfs:
因为图中存在环路,有向图(无向图也一样),我们用dfs进行遍历的时候,可以用一个变量 min 来记录当前路径的代价,再用一个标识数组 (比如book)来标识当前路径上已经选择的节点,这样就能正确处理图中存在环的情况。

实现一:

#include <stdio.h>#include <stdlib.h>#define MAX 50void DFS(int start);void DFS_VISIT(int u, int sum);int graph[MAX + 1][MAX + 1];    //graphint node_record[MAX + 1];       //node identifyint stack_record[MAX + 1];int top = 1;int N, M;       // N * Mint min = 999999;int start_node;     //startint target_node;    //endvoid DFS(int start){    node_record[start] = 1;    stack_record[top] = start;    top++;    for (int i = 1; i <= N; i++) {        if (start == i)            continue;        if (node_record[i] == 1)            continue;        if (graph[start][i] == 999)            continue;         int sum = graph[start][i];        DFS_VISIT(i, sum);    }}void DFS_VISIT(int u, int sum){    if (sum > min)      // do not need to do anymore        return;    node_record[u] = 1;    stack_record[top] = u;    top++;    if (u == target_node) {        printf("path node is : ");        for (int i = 1; i < top; i++)            printf("%d ", stack_record[i]);        printf("\n");        if (sum < min) {            min = sum;         }        node_record[u] = 0;        top--;        return;    }    for (int i = 1; i <= N; i++) {        if (u == i)             continue;        if (node_record[i] == 1)            continue;        if (graph[u][i] == 999)            continue;        node_record[i] = 1;        //stack_record[top] = i;        //top++;        DFS_VISIT(i, sum + graph[u][i]);        node_record[i] = 0;        //top--;    }    node_record[u] = 0;    top--;}int main(){    scanf("%d %d", &N, &M);    //initialize    for (int i = 1; i <= N; i++)        for (int j = 1; j <= N; j++) {            graph[i][j] = 999;        }    for (int i = 1; i <= M; i++) {        int x, y, weight;        scanf("%d %d %d", &x, &y, &weight);        graph[x][y] = weight;    }    //end init    //    start_node = 1;    target_node = N;     DFS(start_node);    printf("min value of path is : %d\n", min);    return 0;}

result1

实现二:

#include <stdio.h>#include <stdlib.h>#define MAX 50void DFS(int current, int distance);int graph[MAX + 1][MAX + 1];int node_record[MAX + 1];int min = 999999;int N, M;int start_node, target_node;int stack_record[MAX + 1];int top = 1;void DFS(int current, int distance){    if (distance > min)        return;    if (current == target_node) {        if (distance < min)             min = distance;        return;    }    for (int i = 1; i <= N; i++) {        if (graph[current][i] != 999 && node_record[i] == 0)  {            node_record[i] = 1;            //stack_record[top] = i;            //top++;            DFS(i, distance + graph[current][i]);            node_record[i] = 0;            //top--;        }    }    return ;}int main(){    scanf("%d %d", &N, &M);    for (int i = 1; i <= N; i++)        for (int j = 1; j <= M; j++) {            if (i == j)                graph[i][j] = 0;            else                graph[i][j] = 999;        }    for (int i = 1; i <= M; i++) {        int x, y, weight;         scanf("%d %d %d", &x, &y, &weight);        graph[x][y] = weight;    }    start_node = 1;    target_node = N;    DFS(1, 0);    printf("min = %d\n", min);    return 0;}

0 0
原创粉丝点击