1. 程式人生 > >杭電OJ-1312 Red and Black

杭電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

.#.........

.#.#######.

.#.#.....#. .#.#.###.#. .#.#

[email protected]#.#. .#.#####.#. .#.......#. .#########. ........... 11 6 ..#..#..#.. ..#..#..#.. ..#..#..### ..#..#..#@. ..#..#..#.. ..#..#..#.. 7 7 ..#.#.. ..#.#.. ###.### [email protected] ###.### ..#.#.. ..#.#.. 0 0

Sample Output

45

59

6

13

有兩種做法,經典一點的就是dfs,從@點出發,每個可以走的點都搜一遍並計數,同時設為已訪問過,防止下次走到會重複計數,程式碼如下:

#include<bits/stdc++.h>
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<bits/stdc++.h>
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);
			}
		}
	}
}