POJ 3026 Borg Maze(BFS+最小生成树)

本文介绍了解决POJ 3026迷宫问题的方法,采用Prim算法结合BFS搜索来寻找最短路径。首先通过BFS计算任意两点间最短距离,再用Prim算法求最小生成树。

题目链接:

http://poj.org/problem?id=3026

解题思路:

感觉这题好坑呀,如果不是看了这封博客,不知道还会哭多久。。。http://blog.youkuaiyun.com/lyy289065406/article/details/6645991

题目大意:

在一个y行 x列的迷宫中,有可行走的通路空格’ ‘,不可行走的墙’#’,还有两种英文字母A和S,现在从S出发,要求用最短的路径L连接所有字母,输出这条路径L的总长度。

一格的长度为1,而且移动的方法只有上、下、左、右,

所以在无任何墙的情况下(但“墙#”是必须考虑的,这里只是为了说明)

任意两个字母之间的距离就是直接把 横坐标之差 加上 纵坐标之差

注意的是,可行的路为 字母 和 空格;不可行的路为 # 和 矩阵范围之外

 
根据题意的“分离”规则,重复走过的路不再计算

因此当使用prim算法求L的长度时,根据算法的特征恰好不用考虑这个问题(源点合并很好地解决了这个问题),L就是最少生成树的总权值W
 
由于使用prim算法求在最小生成树,因此无论哪个点做起点都是一样的,(通常选取第一个点),因此起点不是S也没有关系

所以所有的A和S都可以一视同仁,看成一模一样的顶点就可以了
 
最后要注意的就是 字符的输入。。。输入m,n后,只能用gets吸收。。。getchar会wrong。。。也不知道后台到底是什么样的神数据。。。

剩下的问题关键就是处理 任意两字母间的最短距离,由于存在了“墙#” ,这个距离不可能单纯地利用坐标加减去计算,必须额外考虑,推荐用BFS(广搜、宽搜),这是本题的唯一难点,因为prim根本直接套用就可以了

求 任意两字母间的最短距离 时不能直接用BFS求,

1、必须先把矩阵中每一个允许通行的格看做一个结点(就是在矩阵内所有非#的格都作为图M的一个顶点),对每一个结点i,分别用BFS求出它到其他所有结点的权值(包括其

本身,为0),构造结点图M;

2、然后再加一个判断条件,从图M中抽取以字母为顶点的图,进而构造字母图N

这个判定条件就是当结点图M中的某点j为字母时,把i到j的权值再复制(不是抽离)出来,记录到字母图N的邻接矩阵中

3、剩下的就是对字母图N求最小生成树了

AC代码:

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#define INF 0xfffffff
using namespace std;

const int dx[] = {-1,0,1,0},dy[] = {0,-1,0,1};
struct node{
    int x,y;
};
int n,m;
char maze[110][110];
int vis1[110][110];//记录当前格是否为字母,是第几个字母  
int dist[110][110];//构造结点图的两结点间权值 
int cost[110][110];//vis2和cost记录价值
int vis2[110];//记录最小生成树结点的遍历
int dis[110];

void bfs(int x,int y){
    queue<node>q;
    while(!q.empty())
        q.pop();
    memset(dist,-1,sizeof(dist));
    dist[x][y] = 0;
    q.push(node{x,y});
    while(!q.empty()){
        node cur = q.front();
        q.pop();
        if(vis1[cur.x][cur.y] != -1)
            cost[vis1[x][y]][vis1[cur.x][cur.y]] = dist[cur.x][cur.y];////抽取字母到字母的边权 
        for(int i = 0; i < 4; i++){
            int xx = cur.x+dx[i],yy = cur.y+dy[i];
            if(xx < 0 || xx >= n || yy < 0 || yy >= m || maze[xx][yy] == '#' || dist[xx][yy] != -1)
                continue;
            dist[xx][yy] = dist[cur.x][cur.y]+1;
            q.push(node{xx,yy});
        }
    }
}

int prim(int n)
{
    int i, j, tmp;
    int sum = 0;
    memset(vis2, 0, sizeof(vis2));
    vis2[0] = 1;
    for(i = 0; i < n; i++)
        dis[i] = cost[0][i];
    for(i = 1; i < n; i++)
    {
        int Min = INF;
        for(j = 0; j < n; j++)
        {
            if(!vis2[j] && dis[j] < Min)
                Min = dis[tmp = j];
        }
        vis2[tmp] = 1;
        sum += Min;
        for(j = 0; j < n; j++)
        {
            if(!vis2[j] && dis[j] > cost[tmp][j])
                dis[j] = cost[tmp][j];
        }
    }
    return sum;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&m,&n);
        memset(vis1,-1,sizeof(vis1));
        int sum = 0;
        gets(maze[0]);
        for(int i = 0; i < n; i++){
            gets(maze[i]);
            for(int j = 0; j < m; j++)
                if(maze[i][j] == 'A' || maze[i][j] =='S')
                    vis1[i][j] = sum++;
        }
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                if(vis1[i][j] != -1)
                    bfs(i,j);
        printf("%d\n",prim(sum));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值