description: You have received data from a Bubble bot. You know your task is to make factory facilities, but before you even start, you need to know how big the factory is and how many rooms it has. When you look at the data you see that you have the dimensions of the construction, which is in rectangle shape: N x M. 
  
  Then in the next N lines you have M numbers. These numbers represent factory tiles and they can go from 0 to 15. Each of these numbers should be looked in its binary form. Because from each number you know on which side the tile has walls. For example number 10 in it's binary form is 1010, which means that it has a wall from the North side, it doesn't have a wall from the East, it has a wall on the South side and it doesn't have a wall on the West side. So it goes North, East, South, West. 
  
  It is guaranteed that the construction always has walls on it's edges. The input will be correct. 
  
  Your task is to print the size of the rooms from biggest to smallest. 
  
  Input
  
  The first line has two numbers which are N and M, the size of the construction. Both are integers: 
  
  n (1 ≤ n ≤ 10^3)
  
  m (1 ≤ m ≤ 10^3)
  
  Next N x M numbers represent each tile of construction.
  
  Output
  
  Once you finish processing the data your output consists of one line sorted from biggest to smallest room sizes. 
  
  Example
  
  Input
  
  
  4 5
  9 14 11 12 13
  5 15 11 6 7
  5 9 14 9 14
  3 2 14 3 14
  
  
  Output
  
  
  9 4 4 2 1 
solution: import java.io.BufferedOutputStream;
  import java.io.BufferedReader;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.io.PrintWriter;
  import java.util.ArrayList;
  import java.util.Deque;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.StringTokenizer;
  
  public class Main {
  
  	public static void main(String[] args) {
  		MyScanner sc = new MyScanner();
  		int n = sc.nextInt();
  		int m = sc.nextInt();
  		int[][] walls = scanWalls(sc, n, m);
  		sc.close();
  		List<Integer> result = findRooms(walls, n, m);
  		print(result);
  	}
  
  	private static int[][] scanWalls(MyScanner sc, int n, int m) {
  		int[][] walls = new int[n][m];
  		for (int i = 0; i < n; i++) {
  			for (int j = 0; j < m; j++) {
  				walls[i][j] = sc.nextInt();
  			}
  		}
  		return walls;
  	}
  
  	private static List<Integer> findRooms(int[][] walls, int n, int m) {
  		boolean[][] used = new boolean[n][m];
  		List<Integer> sums = new ArrayList<>();
  		for (int i = 0; i < n; i++) {
  			for (int j = 0; j < m; j++) {
  				if (used[i][j])
  					continue;
  				sums.add(findRoom(walls, used, i, j));
  			}
  		}
  		sums.sort((a, b) -> b.compareTo(a));
  		return sums;
  	}
  
  	private static int findRoom(int[][] walls, boolean[][] used, int iStart, int jStart) {
  		int roomSize = 0;
  		used[iStart][jStart] = true; //mark the cell as used
  		Deque<IntPair> cells = new LinkedList<>();
  		cells.add(pair(iStart, jStart));
  		while (!cells.isEmpty()) {
  			IntPair cell = cells.pop();
  			int i = cell.i;
  			int j = cell.j;
  			roomSize++;
  			
  			IntPair north = getCellIfAllowed(walls, used, i, j, -1, 0);
  			IntPair east  = getCellIfAllowed(walls, used, i, j,  0, 1);
  			IntPair south = getCellIfAllowed(walls, used, i, j,  1, 0);
  			IntPair west  = getCellIfAllowed(walls, used, i, j,  0, -1);
  			
  			if (north != null) cells.add(north);
  			if (east != null) cells.add(east);
  			if (south != null) cells.add(south);
  			if (west != null) cells.add(west);
  		}
  		return roomSize;
  	}
  
  	private static IntPair getCellIfAllowed(int[][] walls, boolean[][] used, int i, int j, int di, int dj) {
  		int current = walls[i][j];
  		int i1 = i + di;
  		int j1 = j + dj;
  		int bitToCheck = getBitToCheck(di, dj);
  		if (((current >> bitToCheck) & 1) == 0) {
  			if (!used[i1][j1]) {
  				used[i1][j1] = true;
  //				System.out.format("Ok path from [%d,%d] to [%d,%d]\n", i, j, i1, j1);
  				return new IntPair(i1, j1);				
  			}
  		}
  		return null;
  	}
  
  	private static int getBitToCheck(int di, int dj) {
  		if (di == -1 && dj == 0 ) return 3;
  		if (di == 0  && dj == 1 ) return 2;
  		if (di == 1  && dj == 0 ) return 1;
  		if (di == 0  && dj == -1) return 0;
  		return 0;
  	}
  
  	private static void print(List<Integer> result) {
  		PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out));
  		for (Integer room : result) {
  			out.write(room + " ");
  		}
  		out.close();
  	}
  
  	private static IntPair pair(int i, int j) {
  		return new IntPair(i, j);
  	}
  
  	private static class IntPair {
  		private int i;
  		private int j;
  
  		private IntPair(int i, int j) {
  			this.i = i;
  			this.j = j;
  		}
  	}
  
  	private static class MyScanner {
  		BufferedReader br;
  		StringTokenizer st;
  
  		public MyScanner() {
  			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());
  		}
  
  		long nextLong() {
  			return Long.parseLong(next());
  		}
  
  		double nextDouble() {
  			return Double.parseDouble(next());
  		}
  
  		String nextLine() {
  			String str = "";
  			try {
  				str = br.readLine();
  			} catch (IOException e) {
  				e.printStackTrace();
  			}
  			return str;
  		}
  		
  		void close() {
  			try {
  				br.close();
  			} catch (IOException e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  		}
  	}
  }
  