1
+ import java .io .BufferedReader ;
2
+ import java .io .InputStreamReader ;
3
+ import java .util .ArrayDeque ;
4
+ import java .util .ArrayList ;
5
+ import java .util .Deque ;
6
+ import java .util .PriorityQueue ;
7
+ import java .util .StringTokenizer ;
8
+
9
+ public class JW_색깔_폭탄 {
10
+
11
+ static class Group implements Comparable <Group > {
12
+ int sy , sx ;
13
+ ArrayList <int []> list = new ArrayList <>(); // 그룹에 해당하는 모든 폭탄의 좌표들
14
+ int redCnt = 0 ;
15
+
16
+ // 처음 시작하는 요소 = 기준
17
+ Group (int sy , int sx ) {
18
+ this .sy = sy ;
19
+ this .sx = sx ;
20
+ }
21
+
22
+ @ Override
23
+ // 우선순위 결정
24
+ public int compareTo (Group o ) {
25
+ if (this .list .size () != o .list .size ())
26
+ return o .list .size () - this .list .size ();
27
+ else if (this .redCnt != o .redCnt )
28
+ return this .redCnt - o .redCnt ;
29
+ else if (this .sy != o .sy )
30
+ return o .sy - this .sy ;
31
+ return this .sx - o .sx ;
32
+ }
33
+ }
34
+
35
+ static int n , m ;
36
+ static int [][] board ;
37
+ static int [] dy = { -1 , 1 , 0 , 0 };
38
+ static int [] dx = { 0 , 0 , -1 , 1 };
39
+
40
+ public static void main (String [] args ) throws Exception {
41
+ BufferedReader br = new BufferedReader (new InputStreamReader (System .in ));
42
+ StringTokenizer st = new StringTokenizer (br .readLine ());
43
+ n = Integer .parseInt (st .nextToken ());
44
+ m = Integer .parseInt (st .nextToken ());
45
+ board = new int [n ][n ];
46
+ for (int i = 0 ; i < n ; i ++) {
47
+ st = new StringTokenizer (br .readLine ());
48
+ for (int j = 0 ; j < n ; j ++)
49
+ board [i ][j ] = Integer .parseInt (st .nextToken ());
50
+ }
51
+ int score = 0 ; // 최종 점수
52
+ while (true ) {
53
+ PriorityQueue <Group > groups = makeGroups (); // 우선순위 큐를 이용해서 우선순위 부여
54
+ // 터트릴 수 없을 경우에 종료
55
+ if (groups .isEmpty () || groups .peek ().list .size () == 1 )
56
+ break ;
57
+ score += Math .pow (groups .peek ().list .size (), 2 ); // 터트리기 전에 점수 계산
58
+ explode (groups .poll ()); // 폭발
59
+ pullToDown (); // 중력 작용
60
+ rotate (); // 반시계 회전
61
+ pullToDown (); // 중력 작용
62
+ }
63
+ System .out .println (score );
64
+ }
65
+
66
+ private static PriorityQueue <Group > makeGroups () {
67
+ PriorityQueue <Group > groups = new PriorityQueue <>();
68
+ boolean [][] visited = new boolean [n ][n ]; // 방문 처리
69
+ for (int y = n - 1 ; y >= 0 ; y --) {
70
+ for (int x = 0 ; x < n ; x ++) {
71
+ // 방문했거나 빨간 폭탄일 경우 건너뜀
72
+ if (visited [y ][x ] || board [y ][x ] <= 0 )
73
+ continue ;
74
+ boolean [][] redVisited = new boolean [n ][n ]; // 빨간 폭탄은 매 그룹마다 포함될 수 있으므로 따로 방문 처리
75
+ Group group = new Group (y , x );
76
+ Deque <int []> dq = new ArrayDeque <>(); // BFS를 위한 덱
77
+ group .list .add (new int [] { y , x });
78
+ dq .offer (new int [] { y , x });
79
+ visited [y ][x ] = true ;
80
+
81
+ // BFS
82
+ while (!dq .isEmpty ()) {
83
+ int [] cur = dq .poll ();
84
+ for (int k = 0 ; k < 4 ; k ++) {
85
+ int ny = cur [0 ] + dy [k ], nx = cur [1 ] + dx [k ];
86
+ if (isValid (ny , nx ) && !visited [ny ][nx ]) {
87
+ // 빨간 폭탄일 경우
88
+ if (board [ny ][nx ] == 0 ) {
89
+ // 방문하지 않은 빨간 폭탄일 경우
90
+ if (!redVisited [ny ][nx ]) {
91
+ group .list .add (new int [] { ny , nx });
92
+ group .redCnt ++;
93
+ dq .offer (new int [] { ny , nx });
94
+ redVisited [ny ][nx ] = true ;
95
+ }
96
+ // 같은 색깔의 폭탄일 경우만
97
+ } else if (board [ny ][nx ] == board [y ][x ]) {
98
+ group .list .add (new int [] { ny , nx });
99
+ dq .offer (new int [] { ny , nx });
100
+ visited [ny ][nx ] = true ;
101
+ }
102
+ }
103
+ }
104
+ }
105
+ groups .offer (group );
106
+ }
107
+ }
108
+ return groups ;
109
+ }
110
+
111
+ // 폭발 함수
112
+ private static void explode (Group group ) {
113
+ ArrayList <int []> list = group .list ;
114
+ for (int [] bomb : list ) {
115
+ board [bomb [0 ]][bomb [1 ]] = -2 ; // 빈 공간으로 변경
116
+ }
117
+ }
118
+
119
+ // 빈 공간이 있을 경우 아래로 당김
120
+ private static void pullToDown () {
121
+ for (int y = n - 1 ; y > 0 ; y --)
122
+ next : for (int x = 0 ; x < n ; x ++)
123
+ if (board [y ][x ] == -2 ) {
124
+ for (int ny = y - 1 ; ny >= 0 ; ny --) {
125
+ // 빈 공간이 아니고 돌이 아닌 다른 수가 나올때까지
126
+ if (board [ny ][x ] != -2 ) {
127
+ if (board [ny ][x ] != -1 )
128
+ swap (y , x , ny , x ); // 스왑
129
+ // 스왑을 했다면 다음 빈 공간 탐색
130
+ continue next ;
131
+ }
132
+ }
133
+ }
134
+ }
135
+
136
+ // 반 시계 방향 회전
137
+ private static void rotate () {
138
+ int [][] next = new int [n ][n ];
139
+ for (int i = 0 ; i < n ; i ++)
140
+ for (int j = 0 ; j < n ; j ++)
141
+ next [n - 1 - j ][i ] = board [i ][j ];
142
+ board = next ;
143
+ }
144
+
145
+ // 배열의 요소를 바꾸는 스왑
146
+ private static void swap (int y , int x , int ny , int nx ) {
147
+ int temp = board [y ][x ];
148
+ board [y ][x ] = board [ny ][nx ];
149
+ board [ny ][nx ] = temp ;
150
+ }
151
+
152
+ // 경계 체크
153
+ private static boolean isValid (int y , int x ) {
154
+ return 0 <= y && y < n && 0 <= x && x < n ;
155
+ }
156
+ }
0 commit comments