TopCoder SRM 533 Div1 Medium: MagicBoard
解法
各列・各行をそれぞれ1つの頂点とみなし,列->行->列->行->...というように全ての頂点を通ることができれば良い.
一筆書きできるかどうかはオイラー閉路の考え方で頂点の次数を調べれば良い.
コード
public class MagicBoard { boolean[][] graph; boolean[] visited; public String ableToUnlock(String[] board) { int R = board.length; int C = board[0].length(); int[] degree = new int[R + C];// 頂点の次数 graph = new boolean[R + C][R + C]; for (int r = 0; r < R; r++) { for (int c = 0; c < C; c++) { if (board[r].charAt(c) == '#') { degree[r]++; degree[c + R]++; graph[r][c + R] = true; graph[c + R][r] = true; } } } // 奇数の行・列の数をカウントする int oddCols = 0; int oddRows = 0; for (int c = 0; c < C; ++c) { if (degree[c + R] % 2 != 0) { oddCols++; } } for (int r = 0; r < R; r++) { if (degree[r] % 2 != 0) { oddRows++; } } // どうやっても無理な時 if (oddCols + oddRows > 2) { return "NO"; } /* * スタートはcol->rowの移動だから, 始点はcolの頂点になる. 奇数の次数をもつcolの頂点が2個あることは問題ないが, * 奇数の次数をもつrowの頂点が2個あると出来ない. */ if (oddRows == 2 && oddCols == 0) { return "NO"; } // 全ての辺が連結かどうか調べる visited = new boolean[R + C]; for (int v = 0; v < R + C; v++) { if (degree[v] > 0) { dfs(v); break; } } for (int v = 0; v < R + C; v++) { if (degree[v] > 0 && !visited[v]) { return "NO"; } } return "YES"; } private void dfs(int start) { if (visited[start]) { return; } visited[start] = true; for (int i = 0; i < graph[start].length; i++) { if (graph[start][i]) { dfs(i); } } return; } }