杭电OJ-1312 Red and Black

Red and Black

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 26912    Accepted Submission(s): 16229

Problem Description

There is a rectangular room, covered with square tiles. Each tile is colored either red or black. A man is standing on a black tile. From a tile, he can move to one of four adjacent tiles. But he can't move on red tiles, he can move only on black tiles.

Write a program to count the number of black tiles which he can reach by repeating the moves described above. 

 

Input

The input consists of multiple data sets. A data set starts with a line containing two positive integers W and H; W and H are the numbers of tiles in the x- and y- directions, respectively. W and H are not more than 20.

There are H more lines in the data set, each of which includes W characters. Each character represents the color of a tile as follows.
'.' - a black tile 
'#' - a red tile 
'@' - a man on a black tile(appears exactly once in a data set) 

 

Output

For each data set, your program should output a line which contains the number of tiles he can reach from the initial tile (including itself).  

 

Sample Input

6 9

....#.

.....#

......

......

......

......

......

#@...#

.#..#.

11 9

.#.........

.#.#######.

.#.#.....#. .#.#.###.#. .#.#..@#.#. .#.#####.#. .#.......#. .#########. ........... 11 6 ..#..#..#.. ..#..#..#.. ..#..#..### ..#..#..#@. ..#..#..#.. ..#..#..#.. 7 7 ..#.#.. ..#.#.. ###.### ...@... ###.### ..#.#.. ..#.#.. 0 0

 

Sample Output

45

59

6

13

 

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1312

有两种做法,经典一点的就是dfs,从@点出发,每个可以走的点都搜一遍并计数,同时设为已访问过,防止下次走到会重复计数,代码如下:

#include
using namespace std;
int n, m, px, py, ans;
char Map[30][30];
int vis[30][30], dir[8] = {0, 1, 0, -1, 1, 0, -1, 0};
void dfs(int x, int y) {
  for(int i = 0; i < 8; i += 2) {
    int nx = x + dir[i], ny = y + dir[i+1];
    if(nx > 0 && nx <= m && ny > 0 && ny <= n && Map[nx][ny] == '.' && !vis[nx][ny]) {
      ans++;
      vis[nx][ny] = 1;
      dfs(nx, ny);
    }
  }
}
int main() {
  while(~scanf("%d%d", &n, &m)) {
    if(n == 0 && m == 0) break;
    memset(Map, 0, sizeof(Map));
    memset(vis, 0, sizeof(vis));
    ans = 1;
    for(int i = 1; i <= m; i++) {
      getchar();
      for(int j =1; j <= n; j++) {
        scanf("%c", &Map[i][j]);
        if(Map[i][j] == '@') {
          px = i;
          py = j;
          Map[i][j] = '.';
        }
      }
    }
    vis[px][py] = 1;
    dfs(px, py);
    printf("%d\n", ans);
  } 
  return 0;
}

还有一种做法就是并查集,用一个结构体给图上所有点编号并记录编号,同时记录该点的值(字符)。对每个点进行遍历,判断某点是否能与其上下左右四个方向的点合并,如果要合并,是向较小编号的那个点合并将其作为根结点,这里要注意的是,一定要遍历两次即进行两次合并才能合并完全。最后遍历结构体,如果有点的编号等于起点的编号(说明是同一个根结点),则进行计数。(这是一个非常好且典型的用并查集来统计联通分块里点的数量的题目)代码如下:

#include
using namespace std;
int n, m, cnt, px, py, ans;
struct point {
  int num;//按照顺序是第几个数,从1开始,同时也记录着该点的根结点是几 
  char ch;
}p[450];
int find(int x) {
  if(x == p[x].num) return p[x].num;
  return p[x].num = find(p[x].num);
}
void join(int x, int y) {
  int fx = find(x), fy = find(y);
  if(fy < fx) p[fx].num = fy;//把联通的点指向最小编号的那个点 
  if(fx < fy) p[fy].num = fx;
}
int main() {
  while(~scanf("%d%d", &n, &m)) {
    if(n == 0 && m == 0) break;
    memset(p, 0, sizeof(p));
    cnt = 1, ans = 0;
    for(int i = 1; i <= m; i++) {
      getchar();
      for(int j =1; j <= n; j++) {
        scanf("%c", &p[cnt].ch);
        p[cnt].num = cnt;//对所有点编号,即把所有点加进了结构体里 
        if(p[cnt].ch == '@') {
          px = i;
          py = j;
          p[cnt].ch = '.';//起点也置为.方便后面进行联通判断 
        }
        cnt++;
      }
    }
    int k = 1;
    for(int i = 1; i <= m; i++) {
      for(int j = 1; j <= n; j++) {
        if(p[k].ch == '.' && i-1 > 0 && p[k-n].ch == '.') join(k, k-n);//上一行有点且为. 
        if(p[k].ch == '.' && i+1 <= m && p[k+n].ch == '.') join(k, k+n);//下一行有点且为. 
        if(p[k].ch == '.' && j-1 > 0 && p[k-1].ch == '.') join(k, k-1);//上一列有点且为. 
        if(p[k].ch == '.' && j+1 <= n && p[k+1].ch == '.') join(k, k+1);//下一列有点且为. 
        k++;
      }
    } 
    k = 1;
    for(int i = 1; i <= m; i++) {//一定要进行两次合并 
      for(int j = 1; j <= n; j++) {
        if(p[k].ch == '.' && i-1 > 0 && p[k-n].ch == '.') join(k, k-n);//上一行有点且为. 
        if(p[k].ch == '.' && i+1 <= m && p[k+n].ch == '.') join(k, k+n);//下一行有点且为. 
        if(p[k].ch == '.' && j-1 > 0 && p[k-1].ch == '.') join(k, k-1);//上一列有点且为. 
        if(p[k].ch == '.' && j+1 <= n && p[k+1].ch == '.') join(k, k+1);//下一列有点且为.
        k++; 
      }
    }
    for(int i = 1; i < cnt; i++)
      if(p[i].num == p[n*(px-1)+py].num) ans++;
    printf("%d\n", ans);
  } 
  return 0;
}

java代码(dfs):

import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.Scanner;

public class Mai {
	
	public static Scanner in = new Scanner(new BufferedInputStream(System.in));
	
	public static int n, m, px, py, ans;
	public static char[][] Map;
	public static int[][] vis; 
	public static int dir[] = {0, 1, 0, -1, 1, 0, -1, 0};
	
	public static void main(String[] args) throws NumberFormatException, IOException {
		while(true) {
			ans = 1;
			String s = in.nextLine();
			String[] ss = s.split(" ");
			n = Integer.parseInt(ss[0]);
			m = Integer.parseInt(ss[1]);
			if(n == 0 && m == 0) break;
			vis = new int[30][30];//清零
			Map = new char[30][30];
			for(int i = 1; i <= m; i++) {
			      String str = in.nextLine();
			      for(int j = 1; j <= n; j++) {
			        Map[i][j] = str.charAt(j-1);
			        if(Map[i][j] == '@') {
			          px = i;
			          py = j;
			          Map[i][j] = '.';
			        }
			      }
			    }
			    vis[px][py] = 1;
			    dfs(px, py);
			    System.out.println(ans);
		}
	}

	public static void dfs(int x, int y) {
		for (int i = 0; i < 8; i += 2) {
			int nx = x + dir[i], ny = y + dir[i + 1];
			if (nx > 0 && nx <= m && ny > 0 && ny <= n && Map[nx][ny] == '.' && vis[nx][ny] == 0) {
				ans++;
				vis[nx][ny] = 1;
				dfs(nx, ny);
			}
		}
	}
}

 

你可能感兴趣的:(ACM之路)