https://www.acmicpc.net/problem/2234
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
static StringBuilder sb = new StringBuilder();
static int N, M;
static int[][] wallNum, roomNumber;
static Room[][] map;
static boolean[][] visited;
static HashSet<Integer>[] neighbor;
static int[] dx = {-1, 0, 1, 0}, dy = {0, -1, 0, 1};
static HashMap<Integer, Integer> nums;
static void input() {
Reader scanner = new Reader();
N = scanner.nextInt();
M = scanner.nextInt();
wallNum = new int[M][N];
map = new Room[M][N];
for(int row = 0; row < M; row++) {
for(int col = 0; col < N; col++) {
wallNum[row][col] = scanner.nextInt();
map[row][col] = new Room();
}
}
}
static void solution() {
makeMap();
int roomNum = 0;
visited = new boolean[M][N];
roomNumber = new int[M][N];
nums = new HashMap<>();
for(int row = 0; row < M; row++) {
for(int col = 0; col < N; col++) {
if(!visited[row][col]) {
roomNum++;
dfs(row, col, roomNum);
}
}
}
sb.append(roomNum).append('\n');
List<Map.Entry<Integer, Integer>> list = new ArrayList<>(nums.entrySet());
Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
@Override
public int compare(Map.Entry<Integer, Integer> l1, Map.Entry<Integer, Integer> l2) {
return l2.getValue() - l1.getValue();
}
});
sb.append(list.get(0).getValue()).append('\n');
findNeighbor(roomNum);
sb.append(getMaxNum(roomNum));
System.out.println(sb);
}
static int getMaxNum(int roomNum) {
int answer = Integer.MIN_VALUE;
for(int room = 1; room <= roomNum; room++) {
for(int n : neighbor[room]) {
int sum = nums.get(room) + nums.get(n);
answer = Math.max(answer, sum);
}
}
return answer;
}
static void findNeighbor(int roomNum) {
neighbor = new HashSet[roomNum + 1];
for(int room = 1; room <= roomNum; room++) neighbor[room] = new HashSet<>();
for(int row = 0; row < M; row++) {
for(int col = 0; col < N; col++) {
int curRoom = roomNumber[row][col];
for(int dir = 0; dir < 4; dir++) {
int cx = row + dx[dir], cy = col + dy[dir];
if(isInMap(cx, cy)) {
if(roomNumber[cx][cy] != curRoom)
neighbor[curRoom].add(roomNumber[cx][cy]);
}
}
}
}
}
static void makeMap() {
for(int row = 0; row < M; row++) {
for(int col = 0; col < N; col++) {
int wall = wallNum[row][col];
for(int num = 8; num > 0; num /= 2) {
if(wall < num) continue;
wall -= num;
if(num == 8) map[row][col].south = true;
else if(num == 4) map[row][col].east = true;
else if(num == 2) map[row][col].north = true;
else if(num == 1) map[row][col].west = true;
}
}
}
}
static void dfs(int x, int y, int roomNum) {
visited[x][y] = true;
roomNumber[x][y] = roomNum;
nums.put(roomNum, nums.getOrDefault(roomNum, 0) + 1);
for(int dir = 0; dir < 4; dir++) {
int cx = x + dx[dir], cy = y + dy[dir];
if(isInMap(cx, cy)) {
if(dir == 0) {
if(map[x][y].north) continue;
} else if(dir == 1) {
if(map[x][y].west) continue;
} else if(dir == 2) {
if(map[x][y].south) continue;
} else {
if(map[x][y].east) continue;
}
if(!visited[cx][cy]) {
dfs(cx, cy, roomNum);
}
}
}
}
static boolean isInMap(int x, int y) {
if(x >= 0 && x < M && y >= 0 && y < N) return true;
return false;
}
static class Room {
boolean west, north, east, south;
public Room() {
this.west = false;
this.north = false;
this.east = false;
this.south = false;
}
public Room(boolean west, boolean north, boolean east, boolean south) {
this.west = west;
this.north = north;
this.east = east;
this.south = south;
}
}
public static void main(String[] args) {
input();
solution();
}
static class Reader {
BufferedReader br;
StringTokenizer st;
public Reader() {
br = new BufferedReader(new InputStreamReader(System.in));
}
String next() {
while(st == null || !st.hasMoreElements()) {
try {
st = new StringTokenizer(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
return st.nextToken();
}
int nextInt() {
return Integer.parseInt(next());
}
}
}