|
| 1 | +## [[POJ](http://poj.org/)] [[INDEX](https://github.com/lyy289065406/POJ-Solving-Reports)] [3026] [[Borg Maze](http://poj.org/problem?id=3026)] |
| 2 | + |
| 3 | +> [Time: 1000MS] [Memory: 65536K] [难度: 初级] [分类: 最小生成树算法] |
| 4 | +
|
| 5 | +------ |
| 6 | + |
| 7 | +## 问题描述 |
| 8 | + |
| 9 | +在一个 y行 x列 的迷宫中,有可行走的通路空格 ` `,不可行走的墙 `#`,还有两种英文字母 `A` 和 `S` ,现在从 `S` 出发,要求用最短的路径 `L` 连接所有字母,输出这条路径 `L` 的总长度。 |
| 10 | + |
| 11 | + |
| 12 | +## 解题思路 |
| 13 | + |
| 14 | +**BFS + Prim** |
| 15 | + |
| 16 | +一格的长度为1,而且移动的方法只有上、下、左、右, |
| 17 | + |
| 18 | +所以在无任何墙的情况下(但“墙#”是必须考虑的,这里只是为了说明) |
| 19 | + |
| 20 | +**任意两个字母之间的距离就是直接把 横坐标之差 加上 纵坐标之差** |
| 21 | + |
| 22 | +**注意**的是: |
| 23 | + |
| 24 | +- ① 可行的路为 字母 和 空格 |
| 25 | +- ② 不可行的路为 # 和 矩阵范围之外 |
| 26 | + |
| 27 | + |
| 28 | +根据题意的**“分离”规则**,重复走过的路不再计算 |
| 29 | + |
| 30 | +因此当使用prim算法求L的长度时,根据算法的特征恰好不用考虑这个问题(源点合并很好地解决了这个问题),L就是最少生成树的总权值W |
| 31 | + |
| 32 | +由于使用prim算法求在最小生成树,因此无论哪个点做起点都是一样的,(通常选取第一个点),因此起点不是S也没有关系 |
| 33 | + |
| 34 | +所以所有的A和S都可以一视同仁,看成一模一样的顶点就可以了 |
| 35 | + |
| 36 | +最后要注意的就是 **字符的输入**: |
| 37 | + |
| 38 | +- cin不读入空字符(包括 空格,换行等) |
| 39 | +- gets读入空格,但不读入换行符) |
| 40 | + |
| 41 | + |
| 42 | +剩下的问题关键就是处理 **任意两字母间的最短距离**,由于存在了“墙#” ,这个距离不可能单纯地利用坐标加减去计算,必须额外考虑,推荐用BFS(广搜、宽搜),这是本题的唯一难点,因为prim根本直接套用就可以了。 |
| 43 | + |
| 44 | + |
| 45 | +在求 任意两字母间的最短距离 时 不能直接 用BFS: |
| 46 | + |
| 47 | +- (1) 必须先把矩阵中每一个允许通行的格看做一个结点(就是在矩阵内所有非#的格都作为图M的一个顶点),对每一个结点i,分别用BFS求出它到其他所有结点的权值(包括其本身,为0),构造结点图M; |
| 48 | +- (2) 然后再加一个判断条件,从图M中抽取以字母为顶点的图,进而构造字母图N。这个判定条件就是当结点图M中的某点j为字母时,把i到j的权值再复制(不是抽离)出来,记录到字母图N的邻接矩阵中 |
| 49 | +- (3) 剩下的就是对字母图N求最小生成树了 |
| 50 | + |
| 51 | + |
| 52 | +## AC 源码 |
| 53 | + |
| 54 | + |
| 55 | +```c |
| 56 | +/*BFS+Prim*/ |
| 57 | + |
| 58 | +//Memory Time |
| 59 | +//368K 0MS |
| 60 | + |
| 61 | +#include<iostream> |
| 62 | +#include<string> |
| 63 | +using namespace std; |
| 64 | + |
| 65 | +const int inf=2501; //无限大,最大迷宫的总长也就2500 |
| 66 | + |
| 67 | +char map[51][51]; //迷宫原图 |
| 68 | +int node[51][51]; //记录当前格是否为字母,是第几个字母 |
| 69 | +int col,row; //当前迷宫的行列数 |
| 70 | +int num; //字母顶点数数目 |
| 71 | +int dist[102][102]; //构造结点图的两结点间权值,理论结点数最多为2500个(每一个允许通行的格为一个结点) |
| 72 | + //但是POJ的数据库允许压缩到101个,哈哈,这样时间和空间复杂度都减少很多 |
| 73 | +int edge[102][102]; //构造字母图的两个字母间的边权,字母数最多为101 |
| 74 | + |
| 75 | +class move |
| 76 | +{ |
| 77 | + public: |
| 78 | + int row,col; |
| 79 | +}mov[4]={{0,-1},{0,1},{-1,0},{1,0}}; //分别对应 上 下 左 右 |
| 80 | + |
| 81 | +void bfs(int i,int j) |
| 82 | +{ |
| 83 | + bool vist[51][51]; //标记当前迷宫某一格是否已被访问 |
| 84 | + int que_x[2500],que_y[2500]; //坐标队列 |
| 85 | + int head=0,tail=0; //队列指针 |
| 86 | + |
| 87 | + /*Initial*/ |
| 88 | + |
| 89 | + memset(dist,0,sizeof(dist)); |
| 90 | + memset(vist,false,sizeof(vist)); |
| 91 | + vist[i][j]=true; |
| 92 | + que_x[tail]=i; |
| 93 | + que_y[tail++]=j; |
| 94 | + |
| 95 | + while(head<tail) |
| 96 | + { |
| 97 | + int x=que_x[head]; |
| 98 | + int y=que_y[head++]; |
| 99 | + if(node[x][y]) |
| 100 | + edge[ node[i][j] ][ node[x][y] ] = dist[x][y]; //抽取字母到字母的边权 |
| 101 | + for(int k=0;k<4;k++) |
| 102 | + { |
| 103 | + int mx=x+mov[k].row; |
| 104 | + int my=y+mov[k].col; |
| 105 | + if(mx>=1 && mx<= row && my>=1 && my<=col) |
| 106 | + if(!vist[mx][my] && map[mx][my]!='#') |
| 107 | + { |
| 108 | + que_x[tail]=mx; |
| 109 | + que_y[tail++]=my; |
| 110 | + vist[mx][my]=true; |
| 111 | + dist[mx][my]=dist[x][y]+1; |
| 112 | + } |
| 113 | + } |
| 114 | + } |
| 115 | + return; |
| 116 | +} |
| 117 | + |
| 118 | +int prim(void) |
| 119 | +{ |
| 120 | + int s=1; |
| 121 | + int m=1; |
| 122 | + bool u[102]; |
| 123 | + u[s]=true; |
| 124 | + |
| 125 | + int min_w; |
| 126 | + int prim_w=0; |
| 127 | + int point; |
| 128 | + int low_dis[102]; |
| 129 | + |
| 130 | + for(int i=1;i<=num;i++) |
| 131 | + { |
| 132 | + low_dis[i]=inf; |
| 133 | + u[i]=false; |
| 134 | + } |
| 135 | + |
| 136 | + while(true) |
| 137 | + { |
| 138 | + if(m==num) |
| 139 | + break; |
| 140 | + |
| 141 | + min_w=inf; |
| 142 | + for(int i=2;i<=num;i++) |
| 143 | + { |
| 144 | + if(!u[i] && low_dis[i]>edge[s][i]) |
| 145 | + low_dis[i] = edge[s][i]; |
| 146 | + if(!u[i] && min_w>low_dis[i]) |
| 147 | + { |
| 148 | + min_w=low_dis[i]; |
| 149 | + point=i; |
| 150 | + } |
| 151 | + } |
| 152 | + s=point; |
| 153 | + u[s]=true; |
| 154 | + prim_w+=min_w; |
| 155 | + m++; |
| 156 | + } |
| 157 | + return prim_w; |
| 158 | +} |
| 159 | + |
| 160 | +int main(int i,int j) |
| 161 | +{ |
| 162 | + int test; |
| 163 | + cin>>test; |
| 164 | + while(test--) |
| 165 | + { |
| 166 | + /*Initial*/ |
| 167 | + |
| 168 | + memset(node,0,sizeof(node)); |
| 169 | + num=0; |
| 170 | + |
| 171 | + /*Input*/ |
| 172 | + |
| 173 | + cin>>col>>row; |
| 174 | + char temp[51]; |
| 175 | + gets(temp); //吃掉cin遗留下来的换行符,我不知道为什么getchar()会AW |
| 176 | + for(i=1;i<=row;i++) |
| 177 | + { |
| 178 | + gets(map[i]); |
| 179 | + for(j=1;j<=col;j++) |
| 180 | + if(map[i][j]=='A'||map[i][j]=='S') |
| 181 | + node[i][j]=++num; |
| 182 | + } |
| 183 | + |
| 184 | + /*BFS-> Structure Maps*/ |
| 185 | + |
| 186 | + for(i=1;i<=row;i++) |
| 187 | + for(j=1;j<=col;j++) |
| 188 | + if(node[i][j]) |
| 189 | + bfs(i,j); //构造结点i,j到其他所有结点的边权(非#的格子就是一个结点) |
| 190 | + |
| 191 | + /*Prim Algorithm & Output*/ |
| 192 | + |
| 193 | + cout<<prim()<<endl; |
| 194 | + } |
| 195 | + return 0; |
| 196 | +} |
| 197 | +``` |
| 198 | +
|
| 199 | +------ |
| 200 | +
|
| 201 | +## 版权声明 |
| 202 | +
|
| 203 | + [-EXP%202016-blue.svg)](http://exp-blog.com) [](https://www.gnu.org/licenses/gpl-3.0) |
| 204 | + |
| 205 | +
|
| 206 | +- Site: [http://exp-blog.com](http://exp-blog.com) |
| 207 | +- Mail: <a href="mailto:[email protected]?subject=[EXP's Github]%20Your%20Question%20(请写下您的疑问)&body=What%20can%20I%20help%20you?%20(需要我提供什么帮助吗?)">[email protected]</a> |
| 208 | +
|
| 209 | +
|
| 210 | +------ |
0 commit comments