복's
[Week30] 스터디 회고 본문
2025 년 첫 스터디 입니다.
연말과 연초는 항상 바쁘기 때문에 대부분의 스터디 인원이 바쁜 개인 일정 소화하느라 힘들어 보이네요... (나 포함)
이번에 출제된 문제는 아래와 같습니다.
- [LV2] 행렬 테두리 회전하기 (프로그래머스)
- [G5] 마법사 상어와 비바라기 (백준)
- [LV3] 이중우선순위큐 (프로그래머스)
[ 📌 내 문제 풀이 with Java ]
👉 행렬 테두리 회전하기
사실 이전에 Python 으로 문제 풀이를 한 적이 있는 문제 였기도 하고, 기억은 안나지만 어느정도 감은 있었던 문제 였습니다.
결국 배열의 좌표를 이동하면서 요구를 수행하는 방법으로 구현하면 문제가 쉽게 풀렸습니다.
조금 고민 되었던 점은 rotate() 수행할 때 방향을 바꾸는 코드를 하나의 코드로 묶고 싶어서 고민한 부분은 있네요.
/**
* Author : Lee In Bok
* Date : 2025.01.11(Sat)
* Runtime : 26.60 ms
* Memory : 113 MB
* Algorithm : Array
*/
package com.Week30.LV2_77485;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
public class LV2_77485 {
public static int[] dx = {0, 1, 0, -1};
public static int[] dy = {1, 0, -1, 0};
public static final int RIGHT = 0;
public static final int DOWN = 1;
public static final int LEFT = 2;
public static final int UP = 3;
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(
Arrays.toString(sol.solution(6, 8, new int[][] {{1, 1, 1, 2}}))
);
}
static class Solution {
public int[] solution(int rows, int columns, int[][] queries) {
List<Integer> ans = new ArrayList<>();
int[][] board = getBoard(rows, columns);
for(int[] query : queries) {
int x1 = query[0] - 1;
int y1 = query[1] - 1;
int x2 = query[2] - 1;
int y2 = query[3] - 1;
ans.add(rotate(board, x1, y1, x2, y2));
}
return ans.stream()
.mapToInt(Integer::intValue)
.toArray();
}
public int rotate(int[][] board, int x1, int y1, int x2, int y2) {
int x = x1;
int y = y1;
int min = Integer.MAX_VALUE;
int prevNum = board[x + 1][y]; // 초기 값은 현재 요소의 아래 위치에 있어서 세팅
// 각 방향마다 경계선 까지 도착하면 종료 하도록 설정
for(int direction = 0; direction < 4; direction++) {
while((direction == RIGHT && y != y2)
|| (direction == DOWN && x != x2)
|| (direction == LEFT && y != y1)
|| (direction == UP && x != x1)
) {
int tmp = prevNum;
min = Math.min(min, prevNum); // 회전할 때 최소 값 찾기
prevNum = board[x][y]; // swap
board[x][y] = tmp; // swap
x += dx[direction];
y += dy[direction];
}
}
return min;
}
// 2 차원 배열 요소 1 부터 차례로 생성
public int[][] getBoard(int rows, int columns) {
return IntStream.range(0, rows)
.mapToObj(row -> IntStream.range(1, columns + 1)
.map(col -> row * columns + col)
.toArray()
)
.toArray(int[][]::new);
}
}
}
👉 마법사 상어와 비바라기
마법사 상어 구현 문제는 시리즈 자체가 조건들이 까다로운 것 같습니다.
대신 까다로운 조건을 코드만 작성하면 통과될 수 있을만큼 생각하기 힘든 엣지 케이스가 있지는 않은 것 같습니다.
구름 이동과 대각선 체크를 동시에 하면 계산일 잘 못 될 수 있는점만 고려하면 헷갈릴만한 부분은 많지 않은 것 같습니다.
스터디원들이 소스 코드 리딩 편하게 주석으로 자세하게 설명 했으니 여기에 적을게 많지는 않네요.
/**
* Author : Lee In Bok
* Date : 2025.01.11(Sat)
* Runtime : 552 ms
* Memory : 24672 KB
* Algorithm : Implementation
*/
package com.Week30.G5_21610;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.stream.IntStream;
public class G5_21610 {
public static int N;
public static int M;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
int[][] board = new int[N][N];
for(int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for(int j = 0; j < N; j++) {
board[i][j] = Integer.parseInt(st.nextToken());
}
}
List<Point> clouds = List.of(
new Point(N - 1, 0),
new Point(N - 1, 1),
new Point(N - 2, 0),
new Point(N - 2, 1)
);
for(int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
int direction = Integer.parseInt(st.nextToken()) - 1;
int dist = Integer.parseInt(st.nextToken());
// 구름 이동
clouds.forEach(e -> e.move(N, board, direction, dist));
// 대각선 체크
clouds.forEach(e -> e.checkDiagonal(N, board));
// 새로운 구름 생성
clouds = getNewClouds(board, clouds);
}
// 배열 모든 요소의 합 출력
System.out.println(Arrays.stream(board)
.flatMapToInt(IntStream::of)
.sum());
}
public static List<Point> getNewClouds(int[][] board, List<Point> oldClouds) {
List<Point> newClouds = new ArrayList<>();
for(int i = 0; i < N; i++) {
for(int j = 0; j < N; j++) {
if(board[i][j] > 1) {
Point point = new Point(i, j);
if(!oldClouds.contains(point)) { // 기존에 구름이 생성되었던 위치는 배제
board[i][j] -= 2; // 구름 생성 하면서 바구니에 물 2 감소
newClouds.add(point);
}
}
}
}
return newClouds;
}
static class Point {
public static final int[] dx = {0, -1, -1, -1, 0, 1, 1, 1};
public static final int[] dy = {-1, -1, 0, 1, 1, 1, 0, -1};
public static final int[] diagonals = {1, 3, 5, 7};
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
// 이동 후 마지막 자리에서 바구니에 물 증가
public Point move(int boundary, int[][] board, int direction, int dist) {
for(int i = 0; i < dist; i++) {
x = calcPoint(x, dx[direction], boundary);
y = calcPoint(y, dy[direction], boundary);
}
board[x][y]++;
return this;
}
// 대각선에 물이 있는 경우 현 위치 물 증가
public Point checkDiagonal(int boundary, int[][] board) {
for(int diagonal : diagonals) {
int checkX = x + dx[diagonal];
int checkY = y + dy[diagonal];
if(isValidDiagonalPoint(checkX, checkY, boundary) && board[checkX][checkY] > 0) {
board[x][y]++;
}
}
return this;
}
// 이동시 양 외각 사이드와 위와 아래가 이어져 있기 때문에 계산 해주는 함수
public int calcPoint(int orgPoint, int movePoint, int boundary) {
// row 이동할 때 오른쪽 사이드에 왼쪽으로 넘어가면서 최대 boundary(N) 값 이상으로 증가 가능하기 때문에 나머지 값 구함
orgPoint = (orgPoint + movePoint) % boundary;
// 왼쪽으로 이동할 때 오른쪽으로 넘어가기 위해서 -1 인지 체크
return (orgPoint == -1) ? boundary - 1 : orgPoint;
}
// 대각선 체크할 때에는 양 끝이 이어져 있어도 체크하지 않기 때문에 대각선 유효 좌표 체크
public boolean isValidDiagonalPoint(int x, int y, int boundary) {
return 0 <= x && x < boundary && 0 <= y && y < boundary;
}
@Override
public boolean equals(Object o) {
Point point = (Point) o;
return x == point.x && y == point.y;
}
}
}
👉 이중우선순위큐
두 개의 우선순위 큐(최소 힙, 최대 힙) 을 이용해서 문제를 풀이 하였는데, TreeSet 을 이용하면 pollLast(), pollFirst() 를 통해서 최대, 최소 값에 접근할 수 있는 쉬운 방법이 있었습니다.
plus) TreeSet 이야기를 하면서 내부 구현에 대해서 이야기 하다가 RB(Red-Black) 트리에 대해서 길지는 않지만 작은 토론도 같이 진행 했습니다.
/**
* Author : Lee In Bok
* Date : 2025.01.11(Sat)
* Runtime : 323.99 ms
* Memory : 116 MB
* Algorithm : Priority Queue
*/
package com.Week30.LV3_42628;
import java.util.*;
public class LV3_42628 {
public static void main(String[] args) {
}
static class Solution {
public final String INSERT = "I";
public int[] solution(String[] operations) {
int[] answer = new int[] {0, 0};
PriorityQueue<Integer> minPq = new PriorityQueue<>(); // 오름차순 큐
PriorityQueue<Integer> maxPq = new PriorityQueue<>((a, b) -> b - a); // 내림차순 큐
StringTokenizer st;
boolean isMax = true;
for(String operation : operations) {
st = new StringTokenizer(operation);
String op = st.nextToken();
int num = Integer.parseInt(st.nextToken());
if(INSERT.equals(op)) {
maxPq.add(num);
} else {
isMax = (num == 1);
if(isMax) {
moveAllElements(maxPq, minPq);
} else {
moveAllElements(minPq, maxPq);
}
}
}
maxPq.addAll(minPq);
if(!maxPq.isEmpty()) {
answer[0] = maxPq.poll();
answer[1] = answer[0];
while(!maxPq.isEmpty()) {
answer[1] = maxPq.poll();
}
}
return answer;
}
public static void moveAllElements(PriorityQueue<Integer> to, PriorityQueue<Integer> from) {
to.addAll(from);
if(to.isEmpty()) {
return;
}
from.clear();
to.poll();
}
}
}
[📌 아쉬운 점...]
스터디에서 항상 아쉬운 점인데 코드 리뷰가를 활발하게 만들 좋은 방안이 아직까지 없어서 너무 아쉽습니다.
(나만 코드 리뷰에 적극적이라서 하지만 강요하고 싶지 않음)
자발적으로 코드 리뷰에 적극적인 참여를 이끌어낼 방법이 있으면 좋겠다 입니다.
어딘가에 장을 한다는건 이러한 고민을 하게 된다는 걸 자연스럽게 알게되는 순간 이네요.
'시간을 보내며... > 스터디 운영...' 카테고리의 다른 글
[Week34] 스터디 회고 (2) | 2025.02.18 |
---|---|
[Week33] 스터디 회고 (1) | 2025.02.11 |
[Week32] 스터디 회고 (1) | 2025.01.29 |
[Week31] 스터디 회고 (1) | 2025.01.20 |
알고리즘 스터디 운영을 해보며... (0) | 2025.01.14 |