提问者:小点点

C语言中使用ASCII艺术和递增字符填充空间的递归洪水填充算法


我有一个任务,我需要写一个递归的洪水填充算法,用递增的字符填充ASCII艺术中的空格。现在,我做了无止境的研究,并认为我理解这个算法的基本原理。然而,当我应用我所学的东西时,输出并没有产生我想要的东西。以下是该计划的大纲

    < li >在“*”代表墙壁的房间中找出“A”。你可以假设总会有一个A。 < li >从A开始,用递增的字符填充整个空间,直到Z,如果达到Z,则继续按Z。输出示例:
*****
*DCB*
***A*
*DCB*
*****

下面是一个示例程序,因为原来的要大得多。


#include <stdio.h>
#include <stdlib.h>

typedef struct room{

    char **array;
    int rows;
    int cols;

} room;

void printRoom(room room);
int *findA(room room);
void floodFill(room room, int x, int y, char paint);

int main(int argc, char *argv[]){

    // Make a new room
    room newRoom;

    // Set the rows and columns
    newRoom.rows = 5;
    newRoom.cols = 5;

    // Allocate memory for the array
    newRoom.array = malloc(newRoom.rows * sizeof(char*));
    for(int i = 0; i < newRoom.rows; i++){
        newRoom.array[i] = malloc(newRoom.cols * sizeof(char));
    }

    // Fill the first array with "*****"
    for(int i = 0; i < newRoom.cols; i++){
        newRoom.array[0][i] = '*';
    }

    // Fill the second array with "*   *"
    newRoom.array[1][0] = '*';
    newRoom.array[1][1] = ' ';
    newRoom.array[1][2] = ' ';
    newRoom.array[1][3] = ' ';
    newRoom.array[1][4] = '*';

    // Fill the third array with "**A*"
    newRoom.array[2][0] = '*';
    newRoom.array[2][1] = '*';
    newRoom.array[2][2] = '*';
    newRoom.array[2][3] = 'A';
    newRoom.array[2][4] = '*';

    // Fill the fourth array with "*   *"
    newRoom.array[3][0] = '*';
    newRoom.array[3][1] = ' ';
    newRoom.array[3][2] = ' ';
    newRoom.array[3][3] = ' ';
    newRoom.array[3][4] = '*';

    // Fill the fifth array with "*****"
    for(int i = 0; i < newRoom.cols; i++){
        newRoom.array[4][i] = '*';
    }
    
    printf("Before\n");
    printRoom(newRoom);

    // Find the A
    int *a = findA(newRoom);

    // Print the A
    printf("A is at %d, %d\n", a[0], a[1]);

    // Flood fill the room
    floodFill(newRoom, a[0], a[1], 'A');

    // Print the room
    printf("\nAfter\n");
    printRoom(newRoom);
    

    return 0;
}

int *findA(room room){

    int *location = malloc(2 * sizeof(int));

    for(int i = 0; i < room.rows; i++){
        for(int j = 0; j < room.cols; j++){
            if(room.array[i][j] == 'A'){
                location[0] = i;
                location[1] = j;
            }
        }
    }

    return location;
}

void floodFill(room room, int x, int y, char paint){

    // If the current position is a wall, return
    if(room.array[x][y] == '*'){
        return;
    }

    // If the current position is already painted, return
    if(room.array[x][y] == paint){
        return;
    }

    if(x < 0 || x >= room.rows || y < 0 || y >= room.cols){
        return;
    }

    // Paint the current position
    room.array[x][y] = paint;

    // Flood fill the left position
    floodFill(room, x, y + 1, paint);

    // Flood fill the right position
    floodFill(room, x, y - 1, paint);

    // Flood fill the top position
    floodFill(room, x + 1, y, paint);

    // Flood fill the bottom position
    floodFill(room, x - 1, y, paint);
}

void printRoom(room room){

    for(int i = 0; i < room.rows; i++){
        for(int j = 0; j < room.cols; j++){
            printf("%c", room.array[i][j]);
        }
        printf("\n");
    }
}

输出

Before
*****
*   *
***A*
*   *
*****
A is at 2, 3

After
*****
*   *
***A*
*   *
*****

我认为问题的一部分是,在上面的版本中,当第一次调用洪水填充时,它会检查当前位置是否等于油漆。在这种情况下,它是,然后立即返回,什么也不做。但是,如果我更改这一行:

floodFill(newRoom, a[0], a[1], 'A');

floodFill(newRoom, a[1], a[0], 'A');

输出

*****
*   *
***A*
*   *
*****
A is at 2, 3

After
*****
*   *
***A*
*AAA*
*****

正如你所看到的,它有点填满了下半部分,但没有填满顶部。我认为这里发生的事情是,由于A在试图上升时已经在那里,所以它退出时,当前位置已经是A。现在我不知道该怎么办,因为我一直被卡住。如果我尝试改变基本情况,我将得到无限递归调用,否则它将什么都不做。此外,我不确定floodFill函数中是否需要两个char参数(char oldPaint,char newPaint)来处理字符的递增。非常感谢您的帮助!


共2个答案

匿名用户

存在以下问题:

>

  • 算法立即停止,因为它发现在位置 xy 处,绘画字符('A')已经存在。为了解决这个问题,我会写一个包装函数(floodFillStart),它首先用空格替换“A”,然后调用 floodFill

    在访问矩阵之前,应检查xy坐标是否超出范围。因此,if语句应该首先出现。

    缺少填充下一个字符的逻辑。

    一旦字符发生变化,检查room. array[x][y]==油漆就不够好:算法会遇到一个具有前面值的字符(洪水来自哪里)。这可以读取room.array[x][y] ! = ' ',因此同时执行墙检查。

    void floodFill(room room, int x, int y, char paint){
        // First this
        if(x < 0 || x >= room.rows || y < 0 || y >= room.cols){
            return;
        }
    
        // If the current position is not a free spot...
        if(room.array[x][y] != ' '){ 
            return;
        }
    
        room.array[x][y] = paint;
    
        // Increment the character to paint with
        if (paint < 'Z') {
            paint++;
        } 
    
        floodFill(room, x, y + 1, paint);
        floodFill(room, x, y - 1, paint);
        floodFill(room, x + 1, y, paint);
        floodFill(room, x - 1, y, paint);
    }
    
    void floodFillStart(room room, int x, int y){
        // Temporarily clear the starting spot
        if (room.array[x][y] == 'A') {
            room.array[x][y] = ' ';
        } 
        floodFill(room, x, y, 'A');
    }
    

    在主程序调用中:

    floodFillStart(room, x, y);
    

  • 匿名用户

    问题是,在开始时,初始单元格已经包含一个<code>A</code>字符,因此函数立即返回。一种方法是在第一次从main调用floodFill之前清除初始单元格,如下所示:

        // Flood fill the room
        newRoom.array[a[0]][a[1]] = ' ';
        floodFill(newRoom, a[0], a[1], 'A');
    

    这将产生以下输出:

    Before
    *****
    *   *
    ***A*
    *   *
    *****
    A is at 2, 3
    
    After
    *****
    *AAA*
    ***A*
    *AAA*
    *****