玩转深度优先搜索算法

小时候玩游戏,有个BOMB人的游戏,把BOMB放在一个空地上,将怪兽炸死,如图:
玩转深度优先搜索算法_第1张图片
 

BOMB的威力只要不碰到墙壁,可以无限延长。那么我们应该把BOMB放哪里可以炸死最多的怪兽呢?

 

这个问题貌似很简单,一个一个地方试下不就知道了吗?是啊,那我们用代码来试下。

 

将图象转成字符,用#表示墙,用G表示怪兽,用.表示空地,最后得到的字符为:

#############

#GG.GGG#GGG.#

###.#G#G#G#G#

#.......#..G#

#G#.###.#G#G#

#GG.GGG.#.GG#

#G#.#G#.#.#.#

##G...G.....#

#G#.#G###.#G#

#...G#GGG.GG#

#G#.#G#G#.#G#

#GG.GGG#G.GG#

#############

那我只要把所有的空地试下就知道哪里放BOMB最好了。

int main()
{
    char a[20][20];
    int i, j, sum , map, p, q = 0;
    int n, m, x, y; //m表示多少行字符,n表示多少列字符
    scanf("%d %d", &n, &m);

    for(i = 0; i <= n - 1; i++)
    {
        scanf("%s", a[i]);
    }

    for (i = 0; i <= n - 1; i++)
    {
        for (j = 0; j <= m - 1; j++)
        {
            //判断当前所在位置是不是平地,是平地才可以放BOMB
            if(a[i][j] != '.')
            {
                continue;
            }

            //当前可以炸0个怪兽
            sum = 0;

            x = i;
            y = j;

            //向上扩展计算消灭的怪兽数
            while(a[x][y] != '#')
            {

                if(a[x][y] == 'G')sum++;
                x--;
            }

            x = i;
            y = j;

            //向下扩展计算消灭的怪兽数
            while(a[x][y] != '#')
            {

                if(a[x][y] == 'G')sum++;
                x++;
            }

            x = i;
            y = j;

            //向左扩展计算消灭的怪兽数
            while(a[x][y] != '#')
            {

                if(a[x][y] == 'G')sum++;
                y--;
            }

            x = i;
            y = j;

            //向右扩展计算消灭的怪兽数
            while(a[x][y] != '#')
            {

                if(a[x][y] == 'G')sum++;
                y++;
            }

            //如果消灭的怪兽大于map,则更新map,并记住所在的坐标
            if(sum > map)
            {
                map = sum;
                p = i;
                q = j;
            }
        }
    }

    printf("将BOMB放在(%d,%d)处,可以消灭最多%d怪兽只\n", p, q, map);
    getchar();
    getchar();
    return 0;
}

 打印结果为:将BOMB放在(1,11)处,可以消灭最多11怪兽只。这种算法为枚举,

 

好像有点不对劲啊,小人根本到不了(1,11),所以那里根本不放了BOMB。所以说我们不能将所有的空地都计算进去,需要将到不了的空地排除。这里该深度优先算法出场了(当然还有其它算法可以解决)。

 

走过迷宫的都知道,我们会沿一条走到头,死路时就回头,回到上一个岔路口,已经确定了一条死路,我们就走另一条路,又是死路,我们就再回到岔路口,两条都是死路了,就回到上上个岔路口,依此循环。

 

这个问题就像小人在走迷宫,直到所有的空地都被小人走过为此。我们用递归来实现这个逻辑:

char a[20][21];
int book[20][20];//标记某个点是否被走过
int max, mx, my, n, m;

//计算当前空地可以炸死的怪兽
int getSum(int i, int j)
{
    int sum, x, y;
    //当前可以炸0个怪兽
    sum = 0;

    x = i;
    y = j;

    //向上扩展计算消灭的怪兽数
    while(a[x][y] != '#')
    {

        if(a[x][y] == 'G')sum++;
        x--;
    }

    x = i;
    y = j;

    //向下扩展计算消灭的怪兽数
    while(a[x][y] != '#')
    {

        if(a[x][y] == 'G')sum++;
        x++;
    }

    x = i;
    y = j;

    //向左扩展计算消灭的怪兽数
    while(a[x][y] != '#')
    {

        if(a[x][y] == 'G')sum++;
        y--;
    }

    x = i;
    y = j;

    //向右扩展计算消灭的怪兽数
    while(a[x][y] != '#')
    {

        if(a[x][y] == 'G')sum++;
        y++;
    }

    return sum;
}


void dfs(int x, int y)
{
    //定义上下左右走动时x,y轴的变化
    int next[4][2] = {{0, 1}, {0, -1}, {1, 0}, { -1, 0}};

    int k, sum, tx, ty;

    sum = getSum(x, y);

    if(sum > max)
    {
        max = sum;
        mx = x;
        my = y;
    }

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

        if(tx < 0 || tx > n - 1 || ty < 0 || ty > n - 1)continue;

        if(a[tx][ty] == '.' && book[tx][ty] == 0)
        {
            book[tx][ty] = 1;//标记走过这个空地了
            dfs(tx, ty);//走向下一个空地
        }
    }
    return ;
}

int main()
{
    int i, startx, starty;

    scanf("%d %d %d %d", &n, &m, &startx, &starty);

    for(i = 0; i <= n - 1; i++)
    {
        scanf("%s", a[i]);
    }

    book[startx][starty] = 1;
    mx = startx;
    my = starty;
    max = getSum(startx, starty);

    dfs(startx, starty);
    printf("将BOMB放置在(%d,%d),最多可以消灭%d个怪兽\n", mx, my, max );
    return 0;
}

 得到结果:将BOMB放置在(7,11),最多可以消灭10个怪兽。

 

 

我再用“栈”来实现:

char a[20][21];

//栈的节点
struct  note
{
    int x;
    int y;
};

//获取当前空地能消灭的怪兽数
int getSum(int i, int j)
{
    int sum, x, y;
    //当前可以炸0个怪兽
    sum = 0;

    x = i;
    y = j;

    //向上扩展计算消灭的怪兽数
    while(a[x][y] != '#')
    {

        if(a[x][y] == 'G')sum++;
        x--;
    }

    x = i;
    y = j;

    //向下扩展计算消灭的怪兽数
    while(a[x][y] != '#')
    {

        if(a[x][y] == 'G')sum++;
        x++;
    }

    x = i;
    y = j;

    //向左扩展计算消灭的怪兽数
    while(a[x][y] != '#')
    {

        if(a[x][y] == 'G')sum++;
        y--;
    }

    x = i;
    y = j;

    //向右扩展计算消灭的怪兽数
    while(a[x][y] != '#')
    {

        if(a[x][y] == 'G')sum++;
        y++;
    }

    return sum;
}


int main()
{
    //初始化一个栈
    struct note que[401];
    int book[20][20];
    int n,m,mx,my,max,top=0, i, startx, starty;

    scanf("%d %d %d %d", &n, &m, &startx, &starty);

    for(i = 0; i <= n - 1; i++)
    {
        scanf("%s", a[i]);
    }

    book[startx][starty] = 1;
    mx = startx;
    my = starty;
    //当起始点加入到栈中
    top++;
    que[top].x = startx;
    que[top].y = starty;
    max = getSum(startx, starty);

    int next[4][2] = {{0, 1}, {0, -1}, {1, 0}, { -1, 0}};

    int k, sum, tx, ty;
    
    //直到栈空了
    while(top != 0)
    {
        sum = getSum(que[top].x, que[top].y);

        if(sum > max)
        {
            max = sum;
            mx = que[top].x;
            my = que[top].y;
        }

        for(k = 0; k <= 3; k++)
        {
            tx = que[top].x + next[k][0];
            ty = que[top].y + next[k][1];

            if(tx < 0 || tx > n - 1 || ty < 0 || ty > n - 1)continue;

            if(a[tx][ty] == '.' && book[tx][ty] == 0)
            {
                book[tx][ty] = 1;
                top++;
                que[top].x = tx;
                que[top].y = ty;
                break;
            }
            //这句很关键,一个空地上下左右都试走过后,将此空地移除栈
            if(k == 3)top--;
        }
    }
    printf("将BOMB放置在(%d,%d),最多可以消灭%d个怪兽\n", mx, my, max );
    return 0;
}

 得到的结果是一样的。

 

这套算法应用十分广泛,可以解决很多问题。作为一名码农还是非常有必要学习一下的,与这对应的还有“广度优先搜索算法”.

你可能感兴趣的:(算法编程)