MDeath-Kid

- M I T & Y
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

USACO 2.1 TEXT Flood Fill Algorithms

Posted on 2011-11-22 19:25  MDeath-Kid  阅读(362)  评论(0编辑  收藏  举报
   Flood Fill

translate by MDK 2011.11.22 18:51

这篇文章说的应该就是很有名的种子填充法。

Sample Problem: Connected Fields

Farmer John's fields are broken into fields, with paths between some of them. Unfortunately, some fields are not reachable from other fields via the paths.

Define a super field is a collection of fields that are all reachable from each other. Calculate the number of superfields.

(计算连通的区域)

The Abstraction

Given: a undirected graph

The component of a graph is a maximal-sized (though not necessarily maximum) subgraph which is connected.

Calculate the component of the graph. 

This graph has three components: {1,4,8}, {2,5,6,7,9}, and {3}. (连通区域)

The Algorithm: Flood Fill

Flood fill can be performed three basic ways: depth-first, breadth-first, and breadth-first scanning. The basic idea is to find some node which has not been assigned to a component and to calculate the component which contains. The question is how to calculate the component.

In the depth-first formulation, the algorithm looks at each step through all of the neighbors of the current node, and, for those that have not been assigned to a component yet, assigns them to this component and recurses on them.

In the breadth-first formulation, instead of recursing on the newly assigned nodes, they are added to a queue.

In the breadth-first scanning formulation, every node has two values: component and visited. When calculating the component, the algorithm goes through all of the nodes that have been assigned to that component but not visited yet, and assigns their neighbors to the current component.

The depth-first formulation is the easiest to code and debug, but can require a stack as big as the original graph. For explicit graphs, this is not so bad, but for implicit graphs, such as the problem presented has, the numbers of nodes can be very large.

The breadth-formulation does a little better, as the queue is much more efficient than the run-time stack is, but can still run into the same problem. Both the depth-first and breadth-first formulations run in N + M time, where N is the number of vertices and M is the number of edges.

The breadth-first scanning formulation, however, requires very little extra space. In fact, being a little tricky, it requires no extra space. However, it is slower, requiring up to N*N + M time, where N is the number of vertices in the graph.

Flood Fill 算法:

FloodFill 可以用三种基本的方法,DFS,BFS,广度优先扫描(?)。基本的思想是找到一些可以缩成一个连通分量的顶点并且计算他有的连通分量。问题是如何计算连通分量。

在DFS中,算法搜索每一步的节点的相邻节点,如果他们还没有归为某个连通区域,就把他们归为这个连通区域,并且在此基础上递归。

在BFS中,不用递归找新节点,而是用队列。

在广度优先扫描法中,每个节点有两个值:属于某连通量的标记 和 是否访问。当计算标记时,算法访问每个节点被标记为这个连通分量但是没有访问的节点,将其标记,并且把他的相邻节点标记为这个连通量。

深搜最容易写和debug,但它需要一个栈。搜索显式图没问题,而对于隐式图,栈可能就存不下了。
广搜稍微好一点,不过也存在问题。搜索大的图它的队列有可能存不下。深搜和广搜时间复杂度均为O(N+M)。其中,N为结点数,M为边数。

广度优先扫描需要的额外空间很少,或者可以说根本不要额外空间,但是它很慢。时间复杂度是O(N^2+M)。

 

Pseudocode for Breadth-First Scanning

This code uses a trick to not use extra space, marking nodes to be visited as in component -2 and actually assigning them to the current component when they are actually visited. 

广度优先扫描 伪代码

这个代码用了一个小技巧是得不需要额外的空间,结点若未访问,将其归入连通子图(-2),

# component(i) denotes the
# component that node i is in
1 function flood_fill(new_component)

2 do
3   num_visited = 0
4   for all nodes i
5     if component(i) = -2
6       num_visited = num_visited + 1
7       component(i) = new_component
8       for all neighbors j of node i
9         if component(j) = nil
10           component(j) = -2
11 until num_visited = 0

12 function find_components

13  num_components = 0
14  for all nodes i
15    component(node i) = nil
16  for all nodes i
17    if component(node i) is nil
18      num_components =
                 num_components + 1
19      component(i) = -2
20      flood_fill(component
                        num_components)

Running time of this algorithm is O(N 2), where N is the numbers of nodes. Every edge is traversed twice (once for each end-point), and each node is only marked once.

算法时间复杂度是 O(N 2),  N 节点个数。每个边访问了两次,每个节点标记了一次。

Execution Example

Consider the graph from above. 

The algorithm starts with all nodes assigned to no component.

Going through the nodes in order first node not assigned to any component yet is vertex 1. Start a new component (component 1) for that node, and set the component of node 1 to -2 (any nodes not shown are unassigned).

Node Component
1 -2

Now, in the flood_fill code, the first time through the do loop, it finds the node 1 is assigned to component -2. Thus, it reassigns it to component 1, signifying that it has been visited, and then assigns its neighbors (node 4) to component -2.

Node Component
1 1
4 -2

As the loop through all the nodes continues, it finds that node 4 is also assigned to component -2, and processes it appropriately as well.

Node Component
1 1
4 1
8 -2

Node 8 is the next to be processed.

Node Component
1 1
4 1
8 1

Now, the for loop continues, and finds no more nodes that have not been assigned yet. Since the until clause is not satisfied ( num_visited = 3), it tries again. This time, no nodes are found, so the function exits and component 1 is complete.

The search for unassigned nodes continues, finding node 2. A new component (component 2) is allocated, node 2 is marked as in component -2, and flood_fill is called.

Node Component
1 1
2 -2
4 1
8 1

Node 2 is found as marked in component -2, and is processed.

Node Component
1 1
2 2
4 1
7 -2
8 1
9 -2

Next, node 7 is processed.

Node Component
1 1
2 2
4 1
5 -2
7 2
8 1
9 -2

Then node 9 is processed.

Node Component
1 1
2 2
4 1
5 -2
7 2
8 1
9 2

The terminating condition does not hold ( num_visited = 3), so the search through for nodes assigned to component -2 starts again. Node 5 is the first one found.

Node Component
1 1
2 2
4 1
5 2
6 -2
7 2
8 1
9 2

Node 6 is the next node found to be in component -2.

Node Component
1 1
2 2
4 1
5 2
6 2
7 2
8 1
9 2

No more nodes are found assigned to component -2, but the terminating condition does not hold, so one more pass through the nodes is performed, finding no nodes assigned to component -2. Thus, the search for unassigned nodes continue from node 2, finding node 3 unassigned.

Node Component
1 1
2 2
3 -2
4 1
5 2
6 2
7 2
8 1
9 2

Node 3 is processed.

Node Component
1 1
2 2
3 3
4 1
5 2
6 2
7 2
8 1
9 2

From here, the algorithm eventually terminates, as there are no more nodes assigned to component -2 and no unassigned nodes. The three components of the graph have been determined, along with the component to which each node belongs.

Problem Cues

Generally, these types of problem are fairly clear. If it asks for sets of "connected" things, it's probably asking for components, in which case flood fill works very well. Often, this is a step in solving the complete problem.

Extensions

The notion of ``components'' becomes muddied when you go to directed graphs.

However, the same flooding idea can be used to determine the points which are reachable from any given point even in a directed graph. At each recursive step, if the point isn't marked already, mark the point as reachable and recurse on all of its neighbors.

Note that to determine which points can reach a given point in a directed graph can be solved the same, by looking at every arc backwards.

Sample Problems

Company Ownership [abridged, IOI 93]

Given: A weighted directed graph, with weights between 0 and 100.

Some vertex A ``owns'' another vertex B if:

  • A = B
  • There is an arc from A to B with weight more than 50.
  • There exists some set of vertices C 1 through C k such that A owns C 1 through Ck, and each vertex has an arc of weight x 1 through x k to vertex B, and x 1 + x 2+ ... + x k > 50.

Find all (a,b) pairs such that a owns b.

Analysis: This can be solved via an adaptation of the calculating the vertices reachable from a vertex in a directed graph. To calculate which vertices vertex A owns, keep track of the ``ownership percentage'' for each node. Initialize them all to zero. Now, at each recursive step, mark the node as owned by vertex A and add the weight of all outgoing arcs to the ``ownership percentages.'' For all percentages that go above 50, recurse into those vertices.

Street Race [IOI 95]

Given: a directed graph, and a start point and an end point.

Find all points p that any path from the start point to the end must travel through p.

Analysis: The easiest algorithm is to remove each point in turn, and check to see if the end point is reachable from the start point. This runs in O(N (M + N)) time. Since the original problem stated that M <= 100, and N <= 50, this will run in time easily.

Cow Tours [1999 USACO National Championship, abridged]

The diameter of a connected graph is defined as the maximum distance between any two nodes of the graph, where the distance between two nodes is defined as the length of the shortest path.

Given a set of points in the plane, and the connections between those points, find the two points which are currently not in the same component, such that the diameter of the resulting component is minimized.

Analysis: Find the components of the original graph, using the method described above. Then, for each pair of points not in the same component, try placing a connection between them. Find the pair that minimizes the diameter.

Connected Fields

Farmer John contracted out the building of a new barn. Unfortunately, the builder mixed up the plans of Farmer John's barn with another set of plans. Farmer John's plans called for a barn that only had one room, but the building he got might have many rooms. Given a grid of the layout of the barn, tell Farmer John how many rooms it has.

Analysis: The graph here is on the non-wall grid locations, with edge between adjacent non-wall locations, although the graph should be stored as the grid, and not transformed into some other form, as the grid is so compact and easy to work with.

街道赛跑 [IOI 95]
已知一个有向图,一个起点和一个终点。
找出所有的p,使得从起点到终点的任何路径都必须经过p。
分析:最简单的算法是枚举p,然后把p删除,看看是否存在从起点到终点的通路。时间复杂度为O(N (M + N))。题目的数据范围是 M <= 100, N <= 50,不会超时。
牛路 [1999 USACO 国家锦标赛]
连通图的直径定义为图中任意两点间距离的最大值,两点间距离定义为最短路的长。
已知平面上一个点集,和这些点之间的连通关系,找出不在同一个连通子图中的两个点,使得连接这两个点后产生的新图有最小的直径。
分析:找出原图的所有连通子图,然后枚举不在同一个连通子图内的每个点对,将其连接,然后找出最小直径。
笨蛋建筑公司
Farmer John 计划建造一个新谷仓。不幸的是,建筑公司把他的建造计划和其他人的建造计划混淆了。Farmer John 要求谷仓只有一个房间,但是建筑公司为他建好的是有许多房间的谷仓。已知一个谷仓平面图,告诉Farmer John 它一共有多少个房间。
分析:随便找一个格子,遍历所有与它连通的格子,得到一个房间。然后再找一个未访问的格子,做同样的工作,直到所有的格子均已访问。虽然题目给你的不是直接的图,但你也可以很容易地对其进行Flood Fill。

 

自己总结:

很多题目并不是直接考强连通量,只是用Flood Fill 预处理成另一个图,或着叫缩点,或是求联通个数,或者在缩点后的图上在求某些东西。

累了,今晚实在不想翻译了。

translate by MDK 2011.11.22.19:24