The Separator in Grid
Description
Given a connected, undirected graph G = (V, E), where V is the vertex set consisting a collection of nodes, and E is the set of edges, each of which connects two nodes from V. A vertex subset S is a separator if the subgraph induced by the vertices in V, but not in S, has two connected components. We shall use the notation [S, W, B] to represent the partition, where the removal of the separator S will give two connected components W and B.
In this problem, we consider the separators in grids. Each node in a grid is connected to its eight neighbors (if they exist). In Figure-1, we illustrate a partition of a 6*6 grid with a 9-point separator (gray nodes in the figure). The nodes on the left of the separator are in set W (white nodes), and the nodes on the right of the separator are in set B (black nodes).
To simplify the problem, you can assume that all the separators referred in this problem satisfy the following restrictions:
- It’s a minimal separator. A separator is minimal if no subset of it forms a separator.
- It begins from a node on the top line of the grid, except the corner (i.e. 30 and 35 in the figures), and ends with a node on the bottom line of the grid, also except the corner (i.e. 0 and 5 in the figures).
- On its way from top to bottom, it can go left, right or down, but never go up.
Now we describe a method to improve a given partition on a grid, through which we can reduce the number of nodes in the separator. This method contains two steps:
- Select several nodes from B and add them into S. Any of the selected nodes must have a left neighbor which is in S.
- Remove several nodes from S (excluding the nodes added in the former step), and add them into W.
After the improvement, we should ensure S is still a separator, and make the number of nodes in S as small as possible. As for Figure-1, we should add 14 and 20 into S, and remove 7, 13, 19 and 25 from S. After that, we obtain a new partition with a 7-point separator shown in Figure-2.
Your task is, given a partition on a grid, to determine the least number of nodes in the separator after the improvement.
Input
There are several test cases. Each case begins with a line containing two integers, N and M (3 <= M, N <= 200). In each of the following N lines, there are M characters, describing the initial partition of the M*N grid. Every character is ‘S’, ‘W’ or ‘B’. It is confirmed that each of these three characters appears at least once in each line, and 'W’s are always on the left of 'S’s.
A test case of N = 0 and M = 0 indicates the end of input, and should not be processed.
Output
For each test case, you should output one line containing one integer, which is the least number of nodes in the separator after the improvement.
Sample Input
6 6
WWSBBB
WSSBBB
WSBBBB
WSBBBB
WSSSBB
WWWSBB
0 0
Sample Output
7
题意: 给出一张字母构成的图S代表初始路径,每个点都有一个权值,权值大小时从0开始一直到所有的格子的数目减1。现要求一条路径使得路径和最小,且S最少。(一开始想到的时A * 搜索,后来因为poj的测评机用的c++的版本太古老,很多的stl里面的结构都不支持,懒得把写好的那些代码修改了,然后从网上看了看题解,写了这篇博客)
题解: 不难发现我们每做一次调整(将格子左移一下)会使得整体减少2。
由上图可以看出,粉色的线所占的格子时7格,而黑线占的格子时9格,很容易找出每往左移一次就会减少两个S所占的格子。
使用两次bfs来探索,第一次探索把S左边的为B的格子都设置为S,然后第二次探索即可找出结果。
c++ AC代码
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MOVE[3][2] = {
{0, 1},//right
{1, 0},//down
{0, -1}//left
};
int M, N;
char map[200][201];
bool vis[200][200];
bool inputMap()
{
scanf("%d%d", &M, &N);
if(M == 0) return false;
while(getchar() != '\n');
for(int i = 0; i < M; ++i) gets(map[i]);
return true;
}
bool isOutOfBounds(int r, int c) // 判断该坐标是否超出范围
{
return r < 0 || r >= M || c < 0 || c >= N;
}
struct Point{
int r, c;
Point(int rr, int cc):r(rr), c(cc){}
};
int bfs()
{
queue<Point> q;
//step 0: initialize
for(int i = 0; i < M; ++i) memset(vis[i], false, N);
//step 1: find start at up, right to left
for(int c = N - 2; c > 0; --c){
if(map[0][c] == 'S'){
q.push(Point(0, c));
vis[0][c] = true;
}
}
//step 2: bfs to find way to bottom
int level = 1;
while(!q.empty()){
for(int n = q.size(); n > 0; --n){
Point now = q.front();
q.pop();
for(int i = 0; i < 3; ++i){
int r = now.r + MOVE[i][0], c = now.c + MOVE[i][1];
if(isOutOfBounds(r, c) || map[r][c] != 'S' || vis[r][c]) continue;
q.push(Point(r, c));
vis[r][c] = true;
}
if(!isOutOfBounds(now.r, now.c+1) && map[now.r][now.c+1] == 'B')
map[now.r][now.c+1] = 'S';
if(now.r + 1 == M) return level;
}
++level;
}
return -1;
}
int main()
{
while(inputMap()){
bfs();
printf("%d\n", bfs());
}
return 0;
}
过程图片展示
-
初始状态(灰色格子表示走过的路径)
-
第一次搜索之后
-
第一次填充后的状态,黄色部分也被填充上了S。 -
第二次搜索开始:首先将最上面两个绿色的格子放到了队列里面。(此时n值是2)
-
先搜索了数值时33的节点,把33的节点从队列中删掉了,同时将27加入到了节点中。
(蓝色代表不在队列中了,把33的节点出队之后n便减1,这时n的值变成了1,接着又遍历32的节点,然后把与32相邻且没用访问过的节点加入到了队列中。
这时n已经减为0了,第一次循环完毕,level加1,即结果加了一个节点。 -
这时队列里面有2个节点,数值为26和27的节点,搜索27,发现与它相邻的节点没有符合条件要求的,所有我们直接跳过。搜索26,我们把25和20又加进了队列中,这时这一次的遍历又完毕了,level又加1(此时level已经达到3)。
-
队列的大小还是2,搜索25,把19进队,搜索20把14进队,level又加1。
-
搜索19把13进队,搜索14,把8进队。完毕,level加1(level此时为5)。
-
搜索13,把7入队,搜索8,把9入队,leve加1(leve==6)。
-
接着搜索7,发现没用符合要求的与它相邻的节点,跳过,继续搜索9把3和10入队。level加1(level==7)。
-
这是最后一步,搜索10,把4入队,搜索3,把4入队,因为数值为3的节点的行下标是5,也就是说它已经是到达终点了,所以直接返回level,没有经过最后的
level++;
语句。
输出的最终结果也就是答案所求(从终点出发倒过来推父节点,即最短路径)。
代码参考自:https://blog.csdn.net/uuuououlcz/article/details/31373961