1与2之间的最短距离

一个m*n的方格纸上,有0、1、2三种数字,可以想像0是面墙,不能通过,1是通道上的一个点,2是储物点,求这个方格纸上每个1的点与离它最近的2之间的距离。(有上下左右四个方向可走。)

比如,一个5*5的格子上有:

0 1 1 0 2
1 0 2 0 1
1 1 0 1 1
0 1 1 1 0
0 1 0 0 0

则每个1到2之间的最短距离为:

N 2 1 N 0
9 N 0 N 1
8 7 N 3 2
N 6 5 4 N
N 7 N N N

用N表示原来的墙,0表示原来的2。


刚看到这题时,很多人都会想从1往2上扫,然后利用之前的结果优化,但怎么利用之前的结果,是个问题。

于是我们需要换一种思路:从2反向找1,一个广搜,是不是就解决了?


好了,不废话,直接贴最终代码了:

#include <stdio.h>  // scanf, printf
#include <stdlib.h> // malloc

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

typedef struct {
    int row, col;
} pos;

pos* new_pos(int row, int col) {
    pos* p = (pos*)malloc(sizeof(pos));
    p->row = row;
    p->col = col;
    return p;
}

void pos_free(pos* p) {
    free(p);
}

typedef struct node {
    void* data;
    struct node* next;
} node, *queue;

node* new_node(void* data) {
    node* n = (node*)malloc(sizeof(node));
    n->data = data;
    n->next = NULL;
    return n;
}

void node_free(node* n) {
    free(n);
}

void queue_push(queue* q, node* n) {
    n->next = *q;
    *q = n;
}

node* queue_pop(queue* q) {
    node* n = *q;
    *q = n->next;
    return n;
}

int queue_empty(queue q) {
    return q == NULL;
}

typedef struct {
    int** grid; // 方格
    int** dist; // 距离
    int   row, col;
    queue twos;
} grid;

int unreach_dist(int row, int col) {
    return row + col;
}

grid* new_grid(int row, int col) {
    grid* g = (grid*)malloc(sizeof(grid));
    g->row = row;
    g->col = col;
    g->twos = NULL;

    g->grid = (int**)malloc((row+2) * sizeof(int*));
    for (int r = 0; r <= row+1; r++) {
        g->grid[r] = (int*)malloc((col+2) * sizeof(int));
    }
    for (int r = 0; r <= row+1; r++) {
        g->grid[r][0] = 0;     // 左竖边为0
        g->grid[row+1][0] = 0; // 右竖边为0
        g->grid[0][r] = 0;     // 上横边为0
        g->grid[0][row+1] = 0; // 下横边为0
    }

    g->dist = (int**)malloc((row+2) * sizeof(int*));
    for (int r = 0; r <= row+1; r++) {
        g->dist[r] = (int*)malloc((col+2) * sizeof(int));
        for (int c = 0; c <= col+1; c++) {
            g->dist[r][c] = unreach_dist(row, col);
        }
    }

    return g;
}

void grid_free(grid* g) {
    for (int r = 0; r <= g->row+1; r++) {
        free(g->dist[r]);
    }
    free(g->dist);

    for (int r = 0; r <= g->row+1; r++) {
        free(g->grid[r]);
    }
    free(g->grid);

    // It is callers responsibility to free g->twos.

    free(g);
}

grid* read_input() {
    int row, col, r, c;
    scanf("%d %d", &row, &col);

    grid* g = new_grid(row, col);
    for (r = 1; r <= row; r++) {
        for (c = 1; c <= col; c++) {
            scanf("%d", &g->grid[r][c]);
        }
    }
    return g;
}

void find_2_slots(grid* g) {
    queue* q = &g->twos;
    for (int r = 1; r <= g->row; r++) {
        for (int c = 1; c <= g->col; c++) {
            if (g->grid[r][c] == 2) {
                g->dist[r][c] = 0;
                queue_push(q, new_node((void*)new_pos(r, c)));
            }
        }
    }
}

void find_nearest_dist(grid *g) {
    while (!queue_empty(g->twos)) {
        queue twos = NULL;
        while (!queue_empty(g->twos)) {
            node* n = queue_pop(&g->twos);
            pos*  p = (pos*)n->data;
            int d = g->dist[p->row][p->col] + 1;
            for (int i = 0; i < 4; i++) {
                int r = p->row + _dirs[i][0];
                int c = p->col + _dirs[i][1];
                if (g->grid[r][c]==1 && d<g->dist[r][c]) {
                    g->dist[r][c] = d;
                    queue_push(&twos, new_node((void*)new_pos(r, c)));
                }
            }
            pos_free(p);
            node_free(n);
        }
        g->twos = twos;
    }
}

void print_dist(grid* g) {
    for (int r = 1; r <= g->row; r++) {
        for (int c = 1; c <= g->col; c++) {
            if (g->dist[r][c] == unreach_dist(g->row, g->col)) {
                printf("N ");
            } else {
                printf("%d ", g->dist[r][c]);
            }
        }
        printf("\n");
    }
}

int main(void) {
    grid* g = read_input();
    find_2_slots(g);
    find_nearest_dist(g);
    print_dist(g);
    grid_free(g);
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值