/*
* Problem :: 2615 / 오목
*
* Kind :: Simulation
*
* Insight
* - 조건 1. 같은 색의 바둑알이 연속적으로 다섯 알이 놓이면 그 색이 이기게 된다
* 조건 2. 하지만 여섯 알 이상이 연속적으로 놓인 경우에는 이긴 것이 아니다.
* 조건 3. 아직 승부가 결정되지 않았을 경우에는 0을 출력한다.
* 조건 4. 검은색 또는 흰색이 이겼을 경우에는 둘째 줄에 연속된 다섯 개의 바둑알 중에서
* 가장 왼쪽에 있는 바둑알
* (연속된 다섯 개의 바둑알이 세로로 놓인 경우, 그 중 가장 위에 있는 것)
* 의 가로줄 번호와, 세로줄 번호를 순서대로 출력한다
* + 정말... 삽질이란 삽질은 다 해본 문제이다
* 억울해서 나름 최적화까지 했는데 거기서도 실수만발이었다
* # 일반적으로 이중 for 문을 이용해서
* 왼쪽에서 오른쪽으로, 위쪽에서 아래쪽으로 바둑판의 각 점들을 방문한다고 하자
* 오목이 가능한 경우는 총 4가지다
* 가로로 5개, 세로로 5개, y=x 대각선으로 5개, y=-x 대각선으로 5개
* -> 문제는...
* O ------
* / | \
* / | \
* / | \
* 처음엔 이렇게 연속된 같은 개수의 알의 개수를 세려고 했는데
* 이 경우 2번의 조건을 위배한다는 것이다
* 검은색 바둑알이 (1,1) ~ (1,6) 까지 여섯개 놓여있다고 하면
* (1,1) 에서는 6개 놓인 것으로 올바르게 세지만
* (1,2) 에서는 5개 놓인 것으로 틀리게 세게 된다
* => 즉...
* \ | /
* \ | /
* ----- O -----
* / | \
* / | \
* 2번 조건을 잘 고려해주기 위해서는 위처럼
* 각 경우마다 반대쪽 방향을 모두 세어주어야 한다
* 가로 - 알의 왼쪽, 오른쪽
* 세로 - 알의 위쪽, 아래쪽
* y=x 대각선 - 1사분면, 3사분면
* y=-x 대각선 - 2사분면, 4사분면
*
* - 그러나 이 경우 센 곳을 또 세게 되어 비효율적이다
* + 그래봤자
* 바둑판 알의 개수 : 19*19
* 가로, 세로, y=x 대각선, y=-x 대각선 : 4
* 각 경우 확인 : 19
* 시간복잡도 = 4 * 19^3 = 27436
* 그냥 다 해봐도 상관없지만...
* # 각 경우에 다음과 같이 번호를 붙여주자
* 가로 - 0
* 세로 - 1
* y=-x 대각선 - 2
* y=x 대각선 - 3
* -> V[y][x][i] = (y,x) 알이 i번 경우에 방문었으면 true, 그렇지 않으면 false
* 그렇다면 V[y][x][i] 를 이전에 방문했을 때
* 그 알이 i번 경우로 승리하지 못했으면
* 이제 i번 경우로 승리할 때 V[y][x][i] 를 사용하는 경우는 없다!
* 는 점을 이용한 것이다 <= 결국 이전에 방문했느냐 안했느냐를 추적하는 것이다
* => 바둑판의 각 점을 왼쪽에서 오른쪽으로, 위에서 아래쪽으로 차례로 방문할 시
* 이제는
* O ------
* / | \
* / | \
* / | \
* V[y][x][i] 를 같이 고려한다면 위의 방향만 고려해도
* 답을 올바르게 구할 수 있다!!!
*
* Point
* - (y,x) 알에서 3번으로 승리했을 때
* 4번의 조건에 따라
* y+4, x-4 를 출력해야되는 것을 주의하자
* + 가운데 공백도 있다... <= 이 공백때문에 또 얼마나 틀렸는지 ㅠㅠ
*/
//
// BOJ
// ver.C++
//
// Created by GGlifer
//
// Open Source
#include <iostream>
using namespace std;
#define endl '\n'
// Set up : Global Variables
int N = 19;
int B[19+1][19+1];
bool V[19+1][19+1][4];
int dy[4] = {+1, +1, 0, +1};
int dx[4] = {0, +1, +1, -1};
// Set up : Functions Declaration
int isWon(int y, int x);
bool isValid(int y, int x);
int main()
{
// Set up : I/O
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Set up : Input
for (int i=1; i<=N; i++)
for (int j=1; j<=N; j++)
cin >> B[i][j];
// Process
for (int i=1; i<=N; i++) {
for (int j=1; j<=N; j++) {
if (B[i][j] != 0) { /* 검은색 또는 흰색 알이 놓여있으면 */
int res = isWon(i, j); /* 오목인지 확인 */
// Control : Output
if (res != -1) { /* 오목이면 */
cout << B[i][j] << endl; /* 검은색 xor 흰색 출력 */
cout << ((res == 3) ? i+4 : i); /* 3번 경우 (y=x 대각선) 예외처리 */
cout << ' ';
cout << ((res == 3) ? j-4 : j); /* 3번 경우 (y=x 대각선) 예외처리 */
exit(0);
}
}
}
}
// Control : Output
cout << 0 << endl; /* 아직 승부가 결정되지 않았음 */
}
// Helper Functions
int isWon(int y, int x)
/* (y,x) 에 놓인 바둑알이 오목인 경우 어떤 모양으로 오목인지에 따라 해당하는 번호를 반환
* 오목이 아닌 경우 -1 을 반환
* 가로로 오목인 경우 - 0
* 세로로 오목인 경우 - 1
* y=-x 대각선으로 오목인 경우 - 2
* y=x 대각선으로 오목인 경우 - 3 */
{
for (int i=0; i<4; i++) {
/* (y,x) 바둑알이 이전에 i번 경우를 확인할 때 방문된적 있으면
* (y,x) 바둑알을 이용한 i번 경우 승리는 불가능하므로 넘어감 */
if (V[y][x][i]) continue;
V[y][x][i] = true; /* 방문처리 */
int cnt = 1; /* i번 경우에서 바둑알의 개수 */
/* 바둑알 세기 */
int ny = y + dy[i];
int nx = x + dx[i];
while (isValid(ny, nx) && B[ny][nx] == B[y][x] && not(V[ny][nx][i])) {
cnt++;
V[ny][nx][i] = true;
ny += dy[i];
nx += dx[i];
}
/* 바둑알이 5개 이면 오목이며 각 경우에 해당하는 수를 반환 */
if (cnt == 5) return i;
}
/* 오목이 아님 */
return -1;
}
bool isValid(int y, int x)
/* (y,x) 좌표가 유효하면 true 를 반환, 그 외 false 를 반환 */
{
return y >= 1 && y <= N && x >= 1 && x <= N;
}