https://www.acmicpc.net/problem/1022
문제를 해결하고 다른 사람들의 풀이를 보았는데 나의 코드만 유난히 장황한 것 같다
그래도 평소 약했던 구현 문제를 해결했으니 기록을 남겨본다
접근 방법
아래의 사진처럼 소용돌이가 좌표(0,0)의 숫자 1을 기준으로 한 라인씩 영역을 넓혀간다고 생각했다
이렇게 했을 때 각 라인별로 기준이 되는 숫자를 찾으려했고
1을 기준으로 왼쪽 위의 숫자들에서 다음과 같은 규칙을 발견했다
- 라인 2
- 숫자 1을 기준으로 다음 라인 왼쪽 위로 갈 때 +4
- 숫자 5를 기준으로 같은 라인 오른쪽 아래로 갈 때 +4
- 라인 3
- 숫자 9를 기준으로 다음 라인 왼쪽 위로 갈 때 +8
- 숫자 17을 기준으로 같은 라인 오른쪽 아래로 갈 때 +8
- 라인 4
- 숫자 25를 기준으로 다음 라인 왼쪽 위로 갈 때 +12
- 숫자 37을 기준으로 같은 라인 오른쪽 아래로 갈 때 +12
해당 규칙을 통해 각 라인별 왼쪽 위에 있는 숫자를 기준으로 삼을 수 있다고 판단하였고
각 라인의 왼쪽 위 값을 구하는 코드는 다음과 같다
static int leftTop(int line){
if(line == 0)
return 1;
return leftTop(line-1) + 4 + 8 * (line - 1);
}
기준이 되는 숫자를 구했으니, 이제 각 좌표의 숫자를 구하는 방법만 생각해 보면 된다
먼저 y, x 좌표가 주어졌을 때 해당 좌표의 value를 구하는 방법을 생각하자
int leftTop = leftTop(line);
int leftBottom = leftTop + 2 * line;
int rightTop = leftTop - 2 * line;
int rightBottom = leftTop + 4 * line;
- 기준이 되는 leftTop을 기준으로 leftBottom, rightTop, rightBottom을 구한다
- 만약 |x|와 |y|의 값이 둘 다 line과 동일하다면 각 값에 맞추어 leftTop 혹은 leftBottome, rightTop, rightBottom을 반환한다
- y == line일 때 라인의 아래쪽에서 rightBottom을 기준으로 value를 구한다
- -y == line일 때 라인의 아래쪽에서 rightTop을 기준으로 value를 구한다
- x == line일 때 라인의 아래쪽에서 rightTop을 기준으로 value를 구한다
- -x == line일 때 라인의 아래쪽에서 leftTop을 기준으로 value를 구한다
해당 로직을 구현한 코드는 다음과 같다
static int calcLeftLine(int leftTop, int line, int y){
line = -line;
while(line!=y){
leftTop++;
line++;
}
return leftTop;
}
static int calcRightLine(int rightTop, int line, int y){
line = -line;
while(line!=y){
rightTop--;
line++;
}
return rightTop;
}
static int calcTopLine(int rightTop, int line, int x){
while(line!=x){
rightTop++;
line--;
}
return rightTop;
}
static int calcBottomLine(int rightBottom, int line, int x){
while(line!=x){
rightBottom--;
line--;
}
return rightBottom;
}
좌표에 있는 값을 반환하는 코드
static int calcPos(int y, int x){
int line = Math.max(Math.abs(y), Math.abs(x));
int leftTop = leftTop(line);
int leftBottom = leftTop + 2 * line;
int rightTop = leftTop - 2 * line;
int rightBottom = leftTop + 4 * line;
if(y == -line && x == -line)
return leftTop;
if(y==line && x == line)
return rightBottom;
if(y==line && x == -line)
return leftBottom;
if(y==-line && x == line)
return rightTop;
if(y == line)
return calcBottomLine(rightBottom, line, x);
if(-y == line)
return calcTopLine(rightTop, line, x);
if(x == line)
return calcRightLine(rightTop, line, y);
if(-x == line)
return calcLeftLine(leftTop, line, y);
return -1;
}
이제 (y, x)를 기준으로 좌표를 구할 수 있으니
반복문을 통해 주어진 범위 내의 좌표를 돌아가며 값을 계산하면 된다
다만 제약조건에 다음과 같은 내용이 존재한다
해당 조건을 만족하기 위해 다음과 같은 절차를 거쳤다
- 좌표별 calcPos값을 문자열로 형변환한다
- 숫자의 최대 길이를 최신화하고 문자열 배열에 값을 저장하며 반복문을 종료한다
- 문자열 배열에서 반복문을 다시 수행하며 최대 숫자 길이에 맞게 공백을 추가하며 StringBuilder에 값을 추가한다
전체 코드는 아래와 같다
해결 답안
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
StringBuilder sb = new StringBuilder();
int r1 = Integer.parseInt(st.nextToken());
int c1 = Integer.parseInt(st.nextToken());
int r2 = Integer.parseInt(st.nextToken());
int c2 = Integer.parseInt(st.nextToken());
int maxLength = 0;
String[][] arr = new String[r2-r1+1][c2-c1+1];
for(int i=r1; i<=r2; i++){
for(int j=c1; j<=c2; j++){
String s = String.valueOf(calcPos(i, j));
maxLength = Math.max(s.length(), maxLength);
arr[i-r1][j-c1] = s;
}
}
for(int i=0; i<arr.length; i++){
for(int j=0; j<arr[0].length; j++){
String s = arr[i][j];
for(int k=s.length(); k<maxLength; k++){
sb.append(' ');
}
sb.append(s).append(' ');
}
sb.append('\n');
}
System.out.println(sb);
br.close();
}
static int calcPos(int y, int x){
int line = Math.max(Math.abs(y), Math.abs(x));
int leftTop = leftTop(line);
int leftBottom = leftTop + 2 * line;
int rightTop = leftTop - 2 * line;
int rightBottom = leftTop + 4 * line;
if(y == -line && x == -line)
return leftTop;
if(y==line && x == line)
return rightBottom;
if(y==line && x == -line)
return leftBottom;
if(y==-line && x == line)
return rightTop;
if(y == line)
return calcBottomLine(rightBottom, line, x);
if(-y == line)
return calcTopLine(rightTop, line, x);
if(x == line)
return calcRightLine(rightTop, line, y);
if(-x == line)
return calcLeftLine(leftTop, line, y);
return -1;
}
static int leftTop(int line){
if(line == 0)
return 1;
return leftTop(line-1) + 4 + 8 * (line - 1);
}
static int calcLeftLine(int leftTop, int line, int y){
line = -line;
while(line!=y){
leftTop++;
line++;
}
return leftTop;
}
static int calcRightLine(int rightTop, int line, int y){
line = -line;
while(line!=y){
rightTop--;
line++;
}
return rightTop;
}
static int calcTopLine(int rightTop, int line, int x){
while(line!=x){
rightTop++;
line--;
}
return rightTop;
}
static int calcBottomLine(int rightBottom, int line, int x){
while(line!=x){
rightBottom--;
line--;
}
return rightBottom;
}
}
'코딩테스트 > 백준' 카테고리의 다른 글
[백준/Java] 14502 - [Gold IV] 연구소 (0) | 2025.06.17 |
---|---|
[백준/Java] 17070 - [Gold V] 파이프 옮기기 1 (0) | 2025.03.23 |
[백준/Java] 1620 - [Silver IV] 나는야 포켓몬 마스터 이다솜 (0) | 2025.02.20 |
[백준/Java] 11660 - [Silver I] 구간 합 구하기 5 (0) | 2025.02.14 |
[백준/Java] 13023 - [Gold V] ABCDE (0) | 2025.02.09 |