제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
SWEA 1812 수정이의 타일자르기입니다.

포인트

개인적으로 해당 문제가 조금 어려웠습니다.

타일을 자를때 하나의 타일에서 여러 타일을 만들 수도 있고, 못만드는 경우를 고려해야합니다.

그래서 저는 잘랐을 때의 타일을 저장하기로 하였습니다.

PriorityQueue를 활용하여 가장 큰 길이를 가진 타일을 나오도록 만들었습니다.

소스코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Collections;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Q1812 {
    static class Tile implements Comparable<Tile> {

        int min, max;

        public Tile(int a, int b) {
            this.min = Math.min(a, b);
            this.max = Math.max(a, b);
        }

        @Override
        public int compareTo(Tile o) {
            return Integer.compare(o.min, this.min);
        }
    }
    private static int tc, N, M;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        tc = Integer.parseInt(br.readLine().trim());
        StringBuilder sb = new StringBuilder();
        StringTokenizer st;
        for (int t = 1; t <= tc; t++) {
            st = new StringTokenizer(br.readLine());
            N = Integer.parseInt(st.nextToken());
            M = Integer.parseInt(st.nextToken());

            st = new StringTokenizer(br.readLine());
            Integer[] length = new Integer[N];
            for (int i = 0; i < N; i++) {
                int b = Integer.parseInt(st.nextToken());
                length[i] = b;
            }
            Arrays.sort(length, Collections.reverseOrder());
            PriorityQueue<Tile> pq = new PriorityQueue<>();
            int ans = 1;
            pq.add(new Tile(M, M));
            for (int i = 0; i < N; i++) {
                Tile tile = pq.poll();
                int len = (1 << length[i]);
                if (tile.min >= len) {
                    pq.add(new Tile(tile.max - len, tile.min));
                    pq.add(new Tile(tile.min - len, len));
                } else {
                    pq.add(tile);
                    pq.add(new Tile(M - len, len));
                    pq.add(new Tile(M, M - len));
                    ans++;
                }
            }

            sb.append("#").append(t).append(" ").append(ans).append("\n");
        }
        System.out.println(sb);
    }
}

코드 설명

static class Tile implements Comparable<Tile> {
    int min, max;

    public Tile(int a, int b) {
        this.min = Math.min(a, b);
        this.max = Math.max(a, b);
    }

    @Override
    public int compareTo(Tile o) {
        return Integer.compare(o.min, this.min); // 큰 min부터 우선
    }
}
  • Tile 클래스 정의
    • 분할된 타일의 가로 세로를 담고, min 기준으로 내림차순 정렬되게 Comparable을 구성받아 구현하였습니다.
    • PriorityQueue에서 가장 적절한 위치에 타일을 배치하기 위한 기준입니다.
Arrays.sort(length, Collections.reverseOrder()); // 큰 타일부터 먼저 배치
PriorityQueue<Tile> pq = new PriorityQueue<>();
int ans = 1;
pq.add(new Tile(M, M));
  • 입력받은 length[i]는 타일의 크기를 2^k으로 표현합니다
  • 큰 타일부터 배치하기 위해 내림차순 정렬합니다.
  • 하나의 큰 정사각형(M×M)을 처음으로 넣어줍니다.
for (int i = 0; i < N; i++) {
    Tile tile = pq.poll(); // 현재 가장 적절한 빈 공간
    int len = (1 << length[i]); // 실제 타일 길이 (2^k)

    if (tile.min >= len) {
        // 현재 타일이 현재 빈 공간에 들어갈 수 있으면
        pq.add(new Tile(tile.max - len, tile.min)); // 오른쪽 빈 공간
        pq.add(new Tile(tile.min - len, len));      // 아래쪽 빈 공간
    } else {
        // 현재 타일이 안 들어가면 새 정사각형 추가
        pq.add(tile); // 원래 꺼낸 공간 다시 넣기
        pq.add(new Tile(M - len, len)); // 새 공간에서 남은 오른쪽
        pq.add(new Tile(M, M - len));   // 새 공간에서 남은 아래쪽
        ans++;
    }
}
  • 타일을 배치하고 남은 공간을 다시 Tile 객체로 분할하여 pq에 저장
  • 만약 현재 가장 큰 빈 공간에도 타일이 안 들어가면, 새로운 정사각형이 필요하다는 것으로 
    • 이 때 ans++ 해서 필요한 정사각형 개수를 증가합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
SWEA 5521 상원이의 생일파티입니다.

포인트

이 문제는 그래프 탐색 문제입니다.

특정 인물(상원이, 번호 1번)을 기준으로 자신의 친구와 친구의 친구까지 초대할 수 있는 사람 수를 구하는 것이 목적입니다.
즉, 깊이 2까지 탐색하는 문제로, 단순한 BFS 또는 DFS가 아니라 범위를 제한한 탐색이 핵심입니다.

소스코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

public class Q5521 {
    private static int tc, N, M;
    private static List<List<Integer>> friends;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        tc = Integer.parseInt(br.readLine().trim());

        StringBuilder sb = new StringBuilder();
        StringTokenizer st;
        for (int t = 1; t <= tc; t++) {
            st = new StringTokenizer(br.readLine());
            N = Integer.parseInt(st.nextToken());
            M = Integer.parseInt(st.nextToken());

            friends = new ArrayList<>();
            for (int i = 0; i <= N; i++) {
                friends.add(new ArrayList<>());
            }

            for (int i = 0; i < M; i++) {
                st = new StringTokenizer(br.readLine());
                int a = Integer.parseInt(st.nextToken());
                int b = Integer.parseInt(st.nextToken());
                friends.get(a).add(b);
                friends.get(b).add(a);
            }

            int sum = 0;
            boolean[] already = new boolean[N + 1];
            already[1] = true;
            for (int num : friends.get(1)) {
                if (!already[num]) {
                    sum++;
                    already[num] = true;
                }
                for (int next : friends.get(num)) {
                    if (!already[next]) {
                        sum++;
                        already[next] = true;
                    }
                }
            }

            sb.append("#").append(t).append(" ").append(sum).append("\n");
        }
        System.out.println(sb);
    }
}
/*
2
6 5
2 3
3 4
4 5
5 6
2 5
6 5
1 2
1 3
3 4
2 3
4 5
 */

코드 설명

for (int i = 0; i < M; i++) {
    st = new StringTokenizer(br.readLine());
    int a = Integer.parseInt(st.nextToken());
    int b = Integer.parseInt(st.nextToken());
    friends.get(a).add(b);
    friends.get(b).add(a);
}
  • 인접 리스트 방식으로 친구 관계를 저장할 friends 리스트를 초기화합니다.
  • 사람 번호는 1번부터 시작하므로 0번은 사용하지 않습니다.
  • 양방향 그래프 형태로 친구 관계를 저장합니다.
for (int num : friends.get(1)) {
    if (!already[num]) {
        sum++;
        already[num] = true;
    }
    for (int next : friends.get(num)) {
        if (!already[next]) {
            sum++;
            already[next] = true;
        }
    }
}
  • 상원이(1번)의 친구(num)와 친구의 친구(next)를 순회하며 초대 가능한 사람을 탐색합니다.
  • 이미 초대한 사람은 already 배열로 체크하여 중복을 방지합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
SWEA 3421 수제 버거 장인입니다.

포인트

중·고등학교 때 배운 부분집합 개념을 떠올려 보면, 원소의 개수가 N개일 때 가능한 모든 부분집합의 수는 2ⁿ임을 알 수 있습니다.

이 문제에서도 모든 음식 조합(부분집합)을 고려하되, 서로 같이 먹으면 안 되는 음식 쌍(충돌 쌍)이 포함된 조합은 제외해야 합니다.

초기에는 DFS를 이용해 완전탐색을 시도했지만, 시간 초과가 발생하였고, 더 빠른 탐색 방식이 필요했습니다.

 

그 대안으로 선택한 것이 비트마스킹(bit masking)입니다.

비트마스킹은 집합의 포함 여부를 비트(0 또는 1)로 표현할 수 있기 때문에, 모든 조합을 효율적으로 순회할 수 있으며,
AND(&) 연산을 통해 충돌 여부도 매우 빠르게 판단할 수 있습니다.

이처럼 비트 연산은 속도가 빠르고, 메모리 효율도 뛰어나기 때문에,
이번 문제와 같이 부분집합을 다루는 경우에 매우 적합한 방법입니다.

소스코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Solution {
    private static int tc, N, M;
    private static boolean[][] disabled;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        tc = Integer.parseInt(br.readLine());
        StringBuilder sb = new StringBuilder();
        StringTokenizer st;
        for (int t = 1; t <= tc; t++) {
            st = new StringTokenizer(br.readLine());
            int N = Integer.parseInt(st.nextToken());
            int M = Integer.parseInt(st.nextToken());
            long[][] conflict = new long[M][2];

            for (int i = 0; i < M; i++) {
                st = new StringTokenizer(br.readLine());
                long a = 1 << (Integer.parseInt(st.nextToken()) - 1);
                long b = 1 << (Integer.parseInt(st.nextToken()) - 1);
                conflict[i][0] = a;
                conflict[i][1] = b;
            }

            long count = 0;
            for (int i = 0; i < (1 << N); i++) {
                boolean check = false;
                for (int j = 0; j < M; j++) {
                    long foodA = conflict[j][0];
                    long foodB = conflict[j][1];
                    if ((i & foodA) != 0 && (i & foodB) != 0) {
                        check = true;
                        break;
                    }
                }
                if (!check) {
                    count++;
                }
            }
            sb.append("#").append(t).append(" ").append(count).append("\n");
        }
        System.out.println(sb);
    }
}
/*
3
3 2
1 2
2 3
3 0
3 3
1 2
1 3
2 3
 */

코드 설명

  • long[][] conflict
    서로 함께 먹을 수 없는 음식 쌍을 저장하는 배열
    • 1 << (번호 - 1) 형태로 비트 마스크로 변환하여 저장합니다.
      (예: 음식 1 → 0001, 음식 3 → 0100)
for (int i = 0; i < (1 << N); i++) {
    boolean check = false;
    for (int j = 0; j < M; j++) {
        long foodA = conflict[j][0];
        long foodB = conflict[j][1];
        if ((i & foodA) != 0 && (i & foodB) != 0) {
            check = true;
            break;
        }
    }
    if (!check) {
        count++;
    }
}
  • i는 0부터 2^N - 1까지의 모든 정수를 나타내며, 각 숫자가 하나의 부분집합을 의미합니다.
    • 예: i = 5 (101₂) → 음식 1과 음식 3을 선택한 경우
  • conflict에 저장된 음식 쌍이 동시에 부분집합에 존재하면 해당 조합은 제외
    • (i & foodA) != 0 && (i & foodB) != 0 → 두 음식 모두 포함되었는지 확인
  • 조건을 만족하는 조합(check == false)만 count++
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
SWEA 6782 현주가 좋아하는 제곱근 놀이입니다.

포인트

해당 문제의 가장 포인트는 Math.sqrt()의 반환값입니다.

먼저 자바의 Math.sqrt() 스펙을 살펴보겠습니다.

/**
 * Returns the correctly rounded positive square root of a
 * {@code double} value.
 * Special cases:
 * <ul><li>If the argument is NaN or less than zero, then the result
 * is NaN.
 * <li>If the argument is positive infinity, then the result is positive
 * infinity.
 * <li>If the argument is positive zero or negative zero, then the
 * result is the same as the argument.</ul>
 * Otherwise, the result is the {@code double} value closest to
 * the true mathematical square root of the argument value.
 *
 * @param   a   a value.
 * @return  the positive square root of {@code a}.
 */
@IntrinsicCandidate
public static native double sqrt(double a);

스펙을 살펴보면 반올림된 양의 제곱근을 반환한다고 되어 있습니다.

예를 들어서 Math.sqrt(9) = 3이지만, Math.sqrt(8) = 2.xx 값으로 나오는 것입니다.

더보기

※ 추가로

  • @IntrinsicCandidate 어노테이션은 JVM HotSpot 컴파일러가 이 메서드를 자동 최적화할 수 있도록 힌트를 주는 표시입니다.
    • 목적은 자바 바이트코드를 native CPU 명령어로 치환하기 위함입니다.(CPU 수준의 명령어로 대체해서 실행)
    • 사용 이유는 빠른 처리를 위한 함수들(Math.log10() 등)에 사용됩니다.
  • native 키워드는 자바가 아닌 다른 언어(C/C++ 등..)으로 작성된 것을 나타내는 키워드입니다.
    • Math.sqrt()는 성능이 매우 중요한 함수이기 때문에,
      Java로 직접 구현하지 않고 플랫폼에 최적화된 C/C++ 코드로 OS 레벨에서 구현해 둔 것을 JNI(Java Native Interface)를 통해 호출하는 구조입니다.

이제 문제를 살펴보면

 

값이 2일때는 0번의 연산

연산을 한번만 하는 것은 4(루트 4)이므로 3은 4로 이동 후 연산을 진행하면 2번의 연산이 진행됩니다.(n+1, 루트 N)

다음 제곱수를 생각했을 때 9는 루트 처리 후 3의 값에 +1하면 됩니다.

 

즉 일반화를 진행하면

  1. 현재 수가 완전제곱수라면: √N 연산 (연산 1회)
  2. 그 외의 경우: +1 연산을 반복해서 완전제곱수로 만든 뒤, √ 연산
  • 완전제곱수가 아니면, +1 연산을 통해 다음 완전제곱수로 이동한 뒤 √ 연산을 합니다.
  • 이 과정을 반복하면서 N이 2가 될 때까지 연산 횟수를 누적합니다.
  • 즉, N → 다음 제곱수 → √ → 반복, 이 과정을 통해 DP 없이도 최적의 연산 횟수를 구할 수 있습니다.

소스코드

public class Q6782 {
    private static int tc;
    private static Long N;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        tc = Integer.parseInt(br.readLine());

        StringBuilder sb = new StringBuilder();
        for (int t = 1; t <= tc; t++) {
            N = Long.parseLong(br.readLine());
            sb.append("#").append(t).append(" ").append(solve(N)).append("\n");
        }
        System.out.println(sb);
    }

    private static long solve(long n) {
        long count = 0;

        while (n != 2) {
            long sqrt = (long) Math.sqrt(n);
            if (sqrt * sqrt == n) {
                n = sqrt;
                count++; // 한 번의 연산
            } else {
                long nextSquare = (sqrt + 1) * (sqrt + 1);
                count += nextSquare - n;
                n = nextSquare;
            }
        }

        return count;
    }
}

코드 설명

  • solve(long n)
    n의 2로 만드는 최소 연산 값을 구하는 함수
    • n이 2가 아닐동안
      • Math.sqrt(n)을 통해 sqrt 값을 구해줍니다.(여기서 기본 스펙이 double이기 때문에 long으로 타입 캐스팅을 진행해줍니다.)
      • n이 제곱수인지를 판단해주기 위해 sqrt * sqrt = n인지 파악합니다
        • 제곱수라면
          • n = sqrt로 치환 후 count를 1 증가합니다.
        • 제곱수가 아니라면
          • 다음 제곱수까지 count를 증가시키고
          • n을 다음 제곱수로 치환합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
SWEA 7793 오! 나의 여신님입니다.

포인트

해당 문제에서는 악마의 손아귀가 먼저 진행되어야 합니다.

만약 악마의 손아귀를 통해서 전파된 곳에 수연이가 이미 있는 곳이라도 사실 크게 의미가 없습니다.

왜냐하면 이미 수연이가 있는 위치는 수연이가 도달한 공간이기 때문에 다음 시간에는 수연이가 이동하기에 부식된 공간과는 수연이의 위치와 무관합니다. 수연이가 고려할 것은 다음 위치에 악마의 손아귀가 있는지 없는지 입니다.

따라서 BFS 탐색을 통해서 문제를 진행합니다.

소스코드


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Q7793 {
    static class Pair {
        int x, y;

        public Pair(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
    private static int tc, N, M, ans;
    private static char[][] map;
    private static int[][] step;
    private static boolean[][] visited;
    private static int[] dx = {-1, 0, 1, 0};
    private static int[] dy = {0, 1, 0, -1};
    private static Pair angle, su;
    private static List<Pair> demons;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        tc = Integer.parseInt(br.readLine());
        StringBuilder sb = new StringBuilder();
        StringTokenizer st;

        for (int t = 1; t <= tc; t++) {
            st = new StringTokenizer(br.readLine());
            N = Integer.parseInt(st.nextToken());
            M = Integer.parseInt(st.nextToken());
            map = new char[N][M];
            step = new int[N][M];
            visited = new boolean[N][M];
            demons = new ArrayList<>();
            ans = Integer.MAX_VALUE;

            for (int i = 0; i < N; i++) {
                String line = br.readLine();
                for (int j = 0; j < M; j++) {
                    map[i][j] = line.charAt(j);
                    if (map[i][j] == 'D') {
                        angle = new Pair(i, j);
                    } else if (map[i][j] == 'S') {
                        su = new Pair(i, j);
                    } else if (map[i][j] == '*') {
                        demons.add(new Pair(i, j));
                    }
                }
            }

            int answer = bfs();

            sb.append("#").append(t).append(" ")
                    .append(answer == -1 ? "GAME OVER" : answer)
                    .append("\n");
        }
        System.out.println(sb);
    }

    static int bfs() {
        Queue<Pair> suyeonQ = new LinkedList<>();
        Queue<Pair> demonQ = new LinkedList<>();
        suyeonQ.add(su);
        demonQ.addAll(demons);

        while (!suyeonQ.isEmpty()) {
            // 1. 악마 먼저 퍼짐
            int demonSize = demonQ.size();
            for (int i = 0; i < demonSize; i++) {
                Pair d = demonQ.poll();
                for (int dir = 0; dir < 4; dir++) {
                    int nx = d.x + dx[dir];
                    int ny = d.y + dy[dir];
                    if (canGo(nx, ny) && map[nx][ny] != 'D') {
                        map[nx][ny] = '*';
                        demonQ.offer(new Pair(nx, ny));
                    }
                }
            }

            // 2. 수연이 이동
            int suSize = suyeonQ.size();
            for (int i = 0; i < suSize; i++) {
                Pair s = suyeonQ.poll();
                for (int dir = 0; dir < 4; dir++) {
                    int nx = s.x + dx[dir];
                    int ny = s.y + dy[dir];
                    if (canGo(nx, ny)) {
                        if (map[nx][ny] == '*') {
                            continue;
                        } else if (map[nx][ny] == 'D') {
                            return step[s.x][s.y] + 1;
                        }
                        visited[nx][ny] = true;
                        step[nx][ny] = step[s.x][s.y] + 1;
                        suyeonQ.offer(new Pair(nx, ny));
                    }
                }
            }
        }
        return -1;
    }

    private static boolean canGo(int x, int y) {
        if (!inRange(x, y)) return false;
        if (visited[x][y] || map[x][y] == 'X' || map[x][y] == '*') return false;

        return true;
    }

    private static boolean inRange(int x, int y) {
        return 0 <= x && x < N && 0 <= y && y < M;
    }
}

/*
2
5 3
D*S
.X.
.X.
.X.
...
5 3
D*S
...
.X.
.X.
...
 */

코드 설명

  • inRange(int x, int y)
    x, y가 격자 내에 존재하는지 판단하는 함수
  • canGo(int x, int y)
    x, y가 갈 수 있는 위치인지 판단하는 함수
    • 격자 내에 존재하거나
    • 방문하지 않았던 점이거나
    • 이나 악마의 손아귀가 아닌 점일 시에 true를 반환합니다.
  • bfs()
    bfs 탐색을 하는 함수
    • suyeonQ, demonQ를 선언합니다.
      • suyeonQ: 수연이의 위치를 저장하는 Queue
      • demonQ: 악마의 손아귀의 위치를 저장하는 Queue
    • suyeonQ.isEmpty()가 아닐동안
      • 악마의 손아귀를 먼저 진행
        • 현재 시점의 큐 크기(demonSize)만큼만 반복하여, 같은 시간대의 악마의 손아귀만 처리하고 다음 단계는 다음 턴에 처리
        • 갈 수 있을 때(canGo(nx, ny) && map[nx][ny] != 여신)
          • demonQ에 저장
      • 수연이 이동
        • 현재 시점의 큐 크기(suSize)만큼만 반복하여, 같은 시간대의 수연이 이동만 처리하고 다음 단계는 다음 턴에 처리 
        • step[][] 배열을 통해 이전 위치 + 1을 저장
        • 만약 다음 canGo에 여신이면 step[s.x][s.y] + 1 반환.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
SWEA 1256 K번째 접미어입니다.

 

해당 문제도 솔직히 효율적인 코드인지는 잘 모르겠습니다만, 제가 활용한 알고리즘을 정리하겠습니다.

포인트

Java의 substring() method를 활용하여, 접미어를 구하고, Collections.sort를 진행합니다.

그 이후 k번째 접미어를 찾아줍니다.

소스코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Q1256 {
    private static int tc, K;
    private static List<String> postfix;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        tc = Integer.parseInt(br.readLine());
        StringBuilder sb = new StringBuilder();
        for (int t = 1; t <= tc; t++) {
            K = Integer.parseInt(br.readLine());
            String line = br.readLine();

            postfix = new ArrayList<>();
            for (int i = 0; i < line.length(); i++) {
                postfix.add(line.substring(i));
            }

            Collections.sort(postfix);

            sb.append("#").append(t).append(" ")
                    .append(postfix.size() >= K ? postfix.get(K-1) : "none").append("\n");
        }
        System.out.println(sb);
    }
}

코드 설명

postfix = new ArrayList<>();
for (int i = 0; i < line.length(); i++) {
    postfix.add(line.substring(i));
}

Collections.sort(postfix);
  • index가 i부터인 substring을 postfix에 넣어주고, 정렬을 진행해줍니다.
  • 그 이후 k번째 접미어를 찾고 만약 없으면 none을 반환해줍니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
SWEA 1259 금속막대입니다.

포인트

문제의 포인트를 잡기 앞서, 조건을 몇개 추가하여야 문제의 해결이 가능합니다.

첫째, 나사들의 중복은 존재하지 않아야합니다. 예를 들어 숫나사의 값들에는 중복이 없으며, 암나사의 값들에는 중복이 없습니다.

둘째, 남는 나사는 없습니다. 즉 모든 나사는 연결이 가능합니다.

 

이러한 조건을 바탕으로 맨 첫번째 숫나사는 어떠한 나사의 암나사와도 일치하지 않음을 알 수 있습니다.

첫번째 나사를 기준으로 암나사의 값과 숫나사의 값이 일치하는 나사를 찾고 그 다음 나사로 반복해주면 문제를 해결할 수 있습니다.

소스코드

import java.io.*;
import java.util.*;

public class Q1259 {
    static int T, N;
    static Map<Integer, Integer> chain;
    static Map<Integer, Integer> revChain;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        T = Integer.parseInt(br.readLine());

        StringBuilder sb = new StringBuilder();

        for (int t = 1; t <= T; t++) {
            N = Integer.parseInt(br.readLine());
            StringTokenizer st = new StringTokenizer(br.readLine());

            chain = new HashMap<>();
            revChain = new HashMap<>();

            for (int i = 0; i < N; i++) {
                int male = Integer.parseInt(st.nextToken());
                int female = Integer.parseInt(st.nextToken());
                chain.put(male, female);
                revChain.put(female, male);  // 역방향도 저장
            }

            // 시작점 찾기: 수나사인데, 누구의 암나사에도 안 쓰인 것
            int start = -1;
            for (int key : chain.keySet()) {
                if (!revChain.containsKey(key)) {
                    start = key;
                    break;
                }
            }

            sb.append("#").append(t);

            // 체인 순서대로 출력
            while (chain.containsKey(start)) {
                int end = chain.get(start);
                sb.append(" ").append(start).append(" ").append(end);
                start = end;
            }

            sb.append("\n");
        }

        System.out.print(sb);
    }
}

코드 설명

  • 암나사와 숫나사의 중복이 존재하지 않기에 이를 저장하는 자료구조 Map을 사용합니다.
    • chain: 숫나사가 key고 암나사가 value인 map
    • revChain: 암나사가 key 숫나가사 value인 map
int start = -1;
for (int key : chain.keySet()) {
    if (!revChain.containsKey(key)) {
        start = key;
        break;
    }
}
  • 숫나사의 value가 어떠한 암나사와도 일치하지 않는 나사 찾기
while (chain.containsKey(start)) {
    int end = chain.get(start);
    sb.append(" ").append(start).append(" ").append(end);
    start = end;
}
  • map을 순회하면서 나사의 연결을 찾습니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
SWEA 1265 달란트2입니다.

포인트

해당 문제는 수학을 사용하는 문제였습니다.

합이 같은 두 수열이라도, 수들의 차이가 적을수록 곱이 더 커집니다.

예를 들어:
- 6을 2와 4로 나누면 곱은 8
- 6을 3과 3으로 나누면 곱은 9 ✅

즉, 곱해지는 수가 서로 비슷할수록 곱의 값은 더 커집니다.
이 성질을 이용하면, 복잡한 탐색 없이 곱의 최댓값을 수학적으로 계산할 수 있습니다.

소스코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Q1265 {
    private static int tc, N, P;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        tc = Integer.parseInt(br.readLine());

        StringBuilder sb = new StringBuilder();
        StringTokenizer st;
        for (int t = 1; t <= tc; t++) {
            st = new StringTokenizer(br.readLine());
            N = Integer.parseInt(st.nextToken());
            P = Integer.parseInt(st.nextToken());

            int q = N / P;
            int r = N % P;

            long result = 1;
            for (int i = 0; i < r; i++) {
                result *= (q + 1);
            }
            for (int i = r; i < P; i++) {
                result *= q;
            }

            sb.append("#").append(t).append(" ").append(result).append("\n");
        }

        System.out.println(sb);
    }
}

코드 설명

  • q는 몫, r은 나머지입니다.
  • r만큼 몫+1 값을 곱해주고
  • p만큼 몫을 곱해줍니다.

+ Recent posts