안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.

이번 포스팅은 백준 문자열 문제 - 단어 뒤집기 2 자바 풀이를 진행하도록 하겠습니다.

 

문제 출처: https://www.acmicpc.net/submit/17413

 

로그인

 

www.acmicpc.net

 

1. 풀이 소스

 

import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();
        
        StringBuilder sb = new StringBuilder();

        boolean flag = false; // '<' 의 시작여부

        int start = -1; // 단어를 뒤집어야할 때 역순으로 순회하기 위한 인덱스 (초기화  -1)
        int end = -1;

        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);

            if (c == '<') {
                if (start != -1) { // 만약 '<' 이전에 'apple<' 처럼 뒤집어야 하는 문자열이 있음
                    for (int j = end; j >= start; j--) sb.append(str.charAt(j));
                    start = -1; // 초기화
                    end = -1;
                }
                flag = true; // flag가 true라면 '>'로 닫히지전까지 유효
            }

            if (flag) sb.append(str.charAt(i)); // 현재 꺽쇠 안이므로 flag
            else {
                if (start == -1) start = i; // 시작 인덱스 설정
                end = i; // 끝 인덱스는 계속 업데이트 (단어 길이가 1일 수 있음)

                if (c == ' ') {
                    for (int j = end - 1; j >= start; j--) sb.append(str.charAt(j));
                    start = -1;
                    sb.append(c);
                }
            }

            if (c == '>') flag = false; // flag false로 설정하여 꺽쇠 해제
        }

        // 꺽쇠 혹은 공백이 없어서 sb에 추가되지 않은 문자열이 있을 수 있으므로
        if (start != -1) {
            for (int j = end; j >= start; j--) sb.append(str.charAt(j));
        }

        System.out.print(sb);
    }
}

 

2. 풀이 중점 사항

 

꺽쇠 내부가 아닌 밖에서 단어를 뒤집어야 할 때, start, end 인덱스를 활용하여 역순으로 sb에 append 하는 것이 중요하였습니다.

단어가 뒤집힐 수 있는 경우를 고려하여, 꺽쇠 밖에 위치하여 공백이 나오거나 다음 char에 '<'를 만난다면, 단어가 있다면 뒤집을 수 있도록 하였습니다. 테스트 케이스 1과 같이 'judge'는 꺽쇠를 만나서 sb에 append 되거나, 공백으로 append 되지 않습니다. 따라서 str.length()에 대한 순회를 마친 후 start 인덱스로 단어가 있는지 판단하였습니다.

이상으로 단어 뒤집기 2 자바 풀이를 마치도록 하겠습니다. 감사합니다!

안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.

이번 포스팅은 백준 문자열 파일 정리 자바 풀이를 작성하고자 합니다.

 

문제 출처: https://www.acmicpc.net/problem/20291

 

20291번: 파일 정리

친구로부터 노트북을 중고로 산 스브러스는 노트북을 켜자마자 경악할 수밖에 없었다. 바탕화면에 온갖 파일들이 정리도 안 된 채 가득했기 때문이다. 그리고 화면의 구석에서 친구의 메시지를

www.acmicpc.net

 

1. 풀이 소스

 

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

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        
        HashMap<String, Integer> map = new HashMap<>(); // 개수를 저장할 map 선언
        HashSet<String> set = new HashSet<>(); // 셋으로 고유한 문자 저장
        
        for (int i = 0; i < n; i++) {
            String type = br.readLine().split("\\.")[1]; // 정규식 문제로 이스케이프로 . 처리

            if (!set.contains(type)) { // 만약 key가 없다면 map에 저장
                set.add(type);
                map.put(type, 1);
            } else map.put(type, map.get(type) + 1); // 이미 값이 있는 경우 + 1
        }

        List<String> sortType = new ArrayList<>(set); // set을 문자열에 addAll
        Collections.sort(sortType); // 오름차순 정렬
        
        StringBuilder sb = new StringBuilder();
        for (String key : sortType) sb.append(key).append(" ").append(map.get(key)).append("\n");
        
        System.out.print(sb);
    }
}

 

 

2. 풀이 중점 사항 

 

.으로 분리하기

split 인자로 들어가는 String 값은 regex 정규식입니다. "."로 문자열을 분리하기 위해서는 "\\."을 사용하여. 을 분할하기 위한 문자로 인식할 수 있도록 해주어야 합니다.

 

HashSet

HashSet에 현재 key가 존재하는지 파악하는 메서드는 set.contains() 입니다. contains()에 값이 있다면 true, 없다면 false가 됩니다. 이를 바탕으로 값을 추가할 수 있습니다.

 

List의 컬렉션 생성자

set에 있는 key를 모두 List에 추가하고자 할 때, "컬렉션 생성자"를 토대로 컬렉션을 생성할 수 있습니다.

new ArrayList<>(set) 으로 샐 성자에 set을 넣어서 컬렉션을 생성하는 방법입니다. 

 

이상으로 파일 정리 자바 풀이를 마치도록 하겠습니다. 감사합니다.!

 

안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.

이번 포스팅은 백준 문자열 문제 - 염색체 자바 풀이를 진행하고자 합니다.

 

문제 출처: https://www.acmicpc.net/problem/9342

 

9342번: 염색체

상근이는 생명과학 연구소에서 염색체가 특정한 패턴인지를 확인하는 일을 하고 있다. 염색체는 알파벳 대문자 (A, B, C, ..., Z)로만 이루어진 문자열이다. 상근이는 각 염색체가 다음과 같은 규칙

www.acmicpc.net

 

1. 풀이 소스

 

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

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

        Solution solution = new Solution();
        for (int i = 0; i < t; i++) {
            solution.setStr(br.readLine());
        }

        System.out.print(solution.getStr());
    }
}

class Solution {

    List<String> strs = new ArrayList<>(); // 문자열 저장 리스트

    void setStr(String str) {
        strs.add(str);
    }

    String getStr() {
        StringBuilder sb = new StringBuilder();
        boolean isInfected; // 조건에 맞는지 판단

        for (String str : strs) {
            isInfected = true;

            if (str == null || str.length() < 3) { // 문자가 null이거나 3개보다 작다면 조건 만족 불가능
                sb.append("Good!").append("\n");
                continue;
            }

            int flag = 0; // 0: A, 1: F, 2: C 3: C가 문자 한 개
            for (int i = 1; i < str.length(); i++) {
                char c = str.charAt(i);

                if (flag == 3) { // 만약 flag == 3인 조건이 true라면 끝에 문자가 하나가 아니라는 것
                    isInfected = false; // 조건 만족 불가
                    break;
                }

                else if (flag == 0) { // A 문자가 유지되는 중
                    if (c == 'F') { // F가 나왔다면 flag++
                        flag++;
                    }
                    else if (c != 'A') { // F와 A가 아니라면 조건 불만족
                        isInfected = false;
                        break;
                    }
                } else if (flag == 1) { // F 문자 유지되는 중
                    if (c == 'C') { // C가 나왔다면 flag++
                        flag++;
                    }
                    else if (c != 'F') { // C와 F가 아니라면 조건 불만족
                        isInfected = false;
                        break;
                    }
                } else if (flag == 2) {  // C 문자 유지 중
                    if (c != 'C') {
                        if (c == 'A' || c == 'B' || c == 'D' || c == 'E' || c =='F') flag++; // 마지막 문자 한 개가 되어야 함
                        else {
                            isInfected = false;
                            break;
                        }
                    }
                }
            }
            if (isInfected && (flag == 2 || flag == 3)) {
                sb.append("Infected!").append("\n");
            } else {
                sb.append("Good").append("\n");
            }

        }

        return sb.toString();
    }
}

 

2. 풀이 중점 사항

 

각 조건은

'첫 번째 문자 -> A -> F -> C -> 끝 문자 혹은 종료'의 순서로 순차적으로 진행되게 됩니다.

따라서, O(n)로 문제를 해결할 수 있었습니다.

flag를 int 타입으로 선언하여, 각 문자를 한 번 순회하면서 같은 문자라면 flag 유지하고,
조건에 맞도록 다음 문자가 이어지면 flag가 변경될 수 있도록 하였습니다.

 

다소 헷갈릴 수 있는 부분은 규칙을 지키는 경우에 Infected!를 출력해야 한다는 점입니다.!

이상으로 백준 염색체 자바 풀이를 마치도록 하겠습니다. 감사합니다!!

 

 

안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.

이번 포스팅은 백준 문자열 문제 - 비밀번호 발음하기 자바 풀이를 진행하고자 합니다.

 

문제 출처: https://www.acmicpc.net/problem/4659

 

4659번: 비밀번호 발음하기

좋은 패스워드를 만드는것은 어려운 일이다. 대부분의 사용자들은 buddy처럼 발음하기 좋고 기억하기 쉬운 패스워드를 원하나, 이런 패스워드들은 보안의 문제가 발생한다. 어떤 사이트들은 xvtp

www.acmicpc.net

 

1. 풀이 소스

 

import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        String str = "";
        
        boolean isPossible; // 가능한 경우 판단 
        boolean flag; // 모음 중 하나는 반드시 포함 
        boolean isVowelSeq; // 모음이 연속인지 ?
        int seq; // 모음 연속 
        char pre; // 이전 문자열 
        
        while ((str = br.readLine()) != null && !str.equals("end")) {
            pre = str.charAt(0);
            seq = 0;
            flag = isVowelSeq = false;
            isPossible = true;
            
            if (str.length() >= 1) { // 문자열이 1개 이상인 경우 pre 설정, flag = true
                if (pre == 'a' || pre == 'e' || pre == 'i' || pre == 'o' || pre =='u') {
                    flag = true;
                    isVowelSeq = true; // 모음인 경우 true, 자음인 경우 false
                } 
                seq++; // seq 증가                    
            }
            
            for (int i = 1; i < str.length(); i++) {
                char c = str.charAt(i);
                
                if (!flag && (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c =='u')) flag = true; // 아직 모음 판단 안한 경우
                
                if (pre == c) { // 3번 같은 글자 연속 두번 불가
                    if (!(pre == 'e' || pre == 'o')) {
                        isPossible = false; // 불가능한 경우 종료
                        break;
                    }
                }
                
                // 2번 판단 
                if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c =='u') {
                    if (isVowelSeq) seq++; // 모음 seq 증가 
                    else { // 이전이 자음이었다면
                        isVowelSeq = true;
                        seq = 1;
                    }
                } else { // 자음이라면
                    if (isVowelSeq) { // 이전이 모음이었다면
                        isVowelSeq = false;
                        seq = 1;
                    } else seq++; // 이전에도 자음이었으므로 seq 증가
                }
                
                if (seq >= 3) { 
                    isPossible = false;
                    break;
                }

                pre = c;
            }
            
            if (flag && isPossible) {
                sb.append("<").append(str).append("> is acceptable.").append("\n");
            } else {
                sb.append("<").append(str).append("> is not acceptable.").append("\n");
            }
        }
        
        System.out.print(sb);
    }
}

 

2. 풀이 중점 사항

 

주어진 3가지 조건을 만족하는 코드를 작성하기 위해 flag, isVowelSeq 등의 boolean 변수를 선언하였습니다.

자음과 모음에 대한 seq를 따로 설정하지 않아도 boolean 변수로 모음은 true, 자음은 false로 설정하여 seq를 한 번에 관리할 수 있었습니다.

 

자세한 코드는 주석처리 하였습니다. 감사합니다!

안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.

이번 포스팅에 앞서, 최근에 제가 가고 싶었던 회사의 코딩테스트가 있었습니다.

준비를 안 한 것은 아니지만, 부족했고 정말 기본이라고 할 수 있는 문자열 문제에서 고생을 해서, 많은 자괴감이 들었습니다.

문자열에 대해서 다시 정리할 필요성을 느끼게 되어서 문자열 문제를 다시 풀어보았습니다.

 

문제 출처: https://www.acmicpc.net/problem/6550

 

6550번: 부분 문자열

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스는 한 줄로 이루어져 있으며, 문자열 s 와 t가 빈칸을 사이에 두고 들어온다. s와 t의 길이는 10만을 넘지 않는다.

www.acmicpc.net

 

1. 풀이 소스 

 

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

public class Main {
    
    static Queue<Character> queue = new ArrayDeque<>();
    
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        
        String str;
        String target;
        String seq;
        StringTokenizer st;
        
        while ((str = br.readLine()) != null && !str.equals("EOF")) { // 종료 조건은 문자열이 null이 아니고 EOF가 아니여야 함
            st = new StringTokenizer(str);
            target = st.nextToken();
            seq = st.nextToken();
            
            for (int i = 0; i < target.length(); i++) {
                queue.add(target.charAt(i)); // 큐에 target 문자열 넣기
            }
            
            for (int i = 0; i < seq.length(); i++) {
                if (queue.isEmpty()) break; // seq를 순회하며 target을 넣은 queue가 비면 종료
                if (queue.peek() == seq.charAt(i)) { // 만약 큐에 있는 문자열과 seq 문자열이 같다면 큐에서 빼기
                    queue.poll();
                }
            }

            if (queue.isEmpty()) { // 큐가 비어있다면 부분 문자열임을 의미 
                sb.append("Yes").append("\n");
            } else {
                while(!queue.isEmpty()) { // 큐를 다시 비워주기 
                    queue.poll();
                }
                sb.append("No").append("\n");
            }
        }
        
        System.out.print(sb);
    }
}

 

 

2. 풀이 중점 사항

 

종료 조건으로 str = br.readLine() && !str.equals("EOF")를 토대로 종료 조건을 처리해주어야 합니다.

부분 문자열을 구성하기 위해 queue를 이용하여 문자열을 담은 후, 큐에서 하나씩 빼며 문자열을 처리하도록 하였습니다

 

이상으로 백준 부분 문자열 문제 풀이를 마치도록 하겠습니다. 감사합니다.!

안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.

이번 포스팅은 백준 투포인터 문제 부분합 자바 풀이를 진행하고자 합니다.

 

문제 출처: https://www.acmicpc.net/problem/1806

 

1806번: 부분합

첫째 줄에 N (10 ≤ N < 100,000)과 S (0 < S ≤ 100,000,000)가 주어진다. 둘째 줄에는 수열이 주어진다. 수열의 각 원소는 공백으로 구분되어져 있으며, 10,000이하의 자연수이다.

www.acmicpc.net

 

1. 풀이 소스

 

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

import static java.lang.Integer.parseInt;

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());

        int n = parseInt(st.nextToken());
        int s = parseInt(st.nextToken());
        PartialSum ps = new PartialSum(n, s);

        st = new StringTokenizer(br.readLine());
        for (int i = 0 ; i < n; i++) {
            ps.setMap(i, parseInt(st.nextToken()));
        }

        ps.run();
        System.out.println(ps.getResult());
    }
}

class PartialSum {
    int n;
    int target;
    int result = Integer.MAX_VALUE;
    int[] map;

    public PartialSum(int n, int target) {
        this.n = n;
        this.target = target;
        map = new int[n + 1];
    }

    void setMap(int i, int value) {
        map[i] = value;
    }

    void run() {
        int length = 0;
        int sum = 0;
        int left = 0;
        int right = 0; 

        while (left <= n && right <= n) {
            if (sum < target) { // 타겟이 더 큰 경우
                sum += map[right++]; // 값을 더한 후, 오른쪽 포인터 한 칸 증가  
            } else {
                sum -= map[left++]; // 현재 있는 값 빼고 왼쪽 포인터 한 칸 증가  
                length = right - left + 1; 
                result = Math.min(length, result);
            }
        }
    }

    int getResult() {
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

// 순서
// 포인터에 위치한 값을 처리하고 포인터 이동
// sum이 target보다 크거나 같다면,
// sum에서 left포인트 값 빼고, left 이동 시킴
// 그 후 right - left한 후 + 1 (개수 이므로)

// sum이 target보다 작다면
// sum에서 right값을 더하고 포인터 이동

// 0 1 2 3 4
// 0     0
//

// target = 3
// p: 1, 3,  sum : 1 + 2
// p: 2, 3   sum : 2    --> length = 3 - 2 + 1
// p: 2, 3 sum(2)

 

 

2. 풀이 중점 사항

 

자연수로된 부분합 문제의 큰 특징은 단조 증가라는 것입니다.

예를 들어, 1, 3, 5, 3, 7 이 있을 때,

시작 수열의 인덱스가 1이고, 끝 인덱스가 3이라고 가정하겠습니다.

그렇다면 합은 3, 5, 3 이 됩니다.

여기서 합을 늘리는 방법은 두가지가 있습니다.

시작 인덱스를 -- 하거나, 끝 인덱스를 ++ 하는 방법입니다.

 

만약 합 12를 만족하는 가장 짧은 연속된 수열을 선택하는 경우가 생긴다면 다음과 같이 선택할 수 있습니다.

5, 3, 7 입니다.

 

투포인터를 활용한다면

s

e                      (12 보다 작으므로 e 포인터 1 증가)

 

1    3

s    e                (12 보다 작으므로 e 포인터 1 증가)

 

1    3    5

s          e            (12 보다 작으므로 e 포인터 1 증가)

 

1    3    5    3

s                 e     (12를 만족하므로 s포인터를 1 증가)

 

3    5    3

s           e         (12 보다 작으므로 e 포인터 1 증가, s를 감소시키지 않은 이유는 이전에 s가 1일 때를 이미 했으므로 (무한 루프 방지))

    

3    5    3    7

s                e    (12를 만족하므로 포인터 s 1 증가)

 

5       3       7

s                e   (12를 만족하므로 포인터 s 1 증가)

 

3       7 

s       e          (12 보다 작으므로 e 포인터 1 증가 --> 인덱스 끝이므로 더 이상 단조 증가 불가 -> 종료)

 

처럼 해결할 수 있습니다.

 

while (left <= n && right <= n) {
    if (sum < target) { // 타겟이 더 큰 경우
        sum += map[right++]; // 값을 더한 후, 오른쪽 포인터 한 칸 증가
    } else {
        sum -= map[left++]; // 현재 있는 값 빼고 왼쪽 포인터 한 칸 증가
        length = right - left + 1;
        result = Math.min(length, result);
    }
}

 

따라서, 만약 타깃보다 값이 작다면 right를 증가시키고,

타깃보다 값이 같거나 크다면 left를 증가시키는 방법으로 문제를 해결할 수 있습니다.

 

이상으로 투포인터 부분합 자바 풀이를 마치도록 하겠습니다. 감사합니다.!

안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.

이번 포스팅은 백준 투포인터 문제 - 두 수의 합 자바 풀이를 진행하도록 하겠습니다.

문제 출처: https://www.acmicpc.net/problem/3273

 

3273번: 두 수의 합

n개의 서로 다른 양의 정수 a1, a2, ..., an으로 이루어진 수열이 있다. ai의 값은 1보다 크거나 같고, 1000000보다 작거나 같은 자연수이다. 자연수 x가 주어졌을 때, ai + aj = x (1 ≤ i < j ≤ n)을 만족하는

www.acmicpc.net

 

1. 풀이 소스

 

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

import static java.lang.Integer.parseInt;

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

        TwoSum ts = new TwoSum(n);
        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) {
            ts.setMap(i, parseInt(st.nextToken()));
        }
        ts.setSum(parseInt(br.readLine()));
        ts.run();
        System.out.println(ts.getPair());;
    }
}

class TwoSum {

    int n; // n개의 입력
    int[] map; // 입력 배열
    int target; // 목표 합
    int pair; // 순서쌍 개수

    public TwoSum(int n) {
        this.n = n;
        map = new int[n];
    }

    void setMap(int i, int value) {
        map[i] = value;
    }

    void run() {
        Arrays.sort(map); // 오름차순 정렬하여 투포인터가 가능하도록 설정
        if (map.length == 1) return; // n이 하나인 경우 불가

        int left = 0; // 시작점
        int right = n - 1; // 끝 점

        while(left < right) { // 두 좌표가 교차된다면 종료 (left -> right, right -> left는 두 참조 이름만 바뀐 것이기 때문)

            int value = map[left] + map[right];

            if (target > value) left++; // 만약 목표보다 값이 작다면 left를 증가시켜서 합이 커지도록
            else if (target < value) right--; // 목표보다 크다면 right를 줄여서 합이 작아지도록
            else {
                pair++;
                left++; // left를 증가시켜서 무한루프를 막고 시작점을 현재 인덱스 다음으로 넘겨줌
            }
        }
    }

    void setSum(int target) {
        this.target = target;
    }

    int getPair() {
        return pair;
    }
}

 

2. 풀이 중점 사항 

 

일반적으로 두 수의 합 순서쌍을 구하는 경우는 이중 포문으로 구성하면 빅오 n^2이 나옵니다.

투포인터를 활용한다면 빅오 n으로 문제를 해결할 수 있습니다. 

 

오름차순 된 배열의 각 양 끝점을 포인터로 설정하고, 

목표로 하는 값 K보다 값이 작다면, left 포인터를 증가시킵니다.

 

배열은 오름차순 되어있기 때문에 1, 2, 3, 4, 5처럼 인덱스가 커질수록 단조 증가처리 됩니다.

따라서, 목표로 하는 값보다 작으므로 left를 증가시켜서 값을 늘리고,

늘렸더니 값이 다시 커진다면, right를 감소시켜서 값을 줄이는 방법입니다.

 

이러한 과정을 통해 원하는 k에 수렵하는 포인터 쌍을 구할 수 있습니다.

 

안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다. 

이번 포스팅은 백준 DP문제 - 상자넣기 자바 풀이를 진행하도록 하겠습니다.

문제 출처: https://www.acmicpc.net/problem/1965

 

1965번: 상자넣기

정육면체 모양의 상자가 일렬로 늘어서 있다. 상자마다 크기가 주어져 있는데, 앞에 있는 상자의 크기가 뒤에 있는 상자의 크기보다 작으면, 앞에 있는 상자를 뒤에 있는 상자 안에 넣을 수가

www.acmicpc.net

 

1. 풀이 소스

 

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

import static java.lang.Integer.parseInt;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = parseInt(br.readLine());
        Box box = new Box(n);

        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) {
            box.setMap(i, parseInt(st.nextToken()));
        }

        box.run();
        System.out.println(box.getResult());
    }

}

class Box {
    int n;
    int[] map; // 값 저장
    int[] dp; // 최대 몇개를 넣을 수 있는지 저장하는 DP
    int result;
    public Box(int n) {
        this.n = n;
        map = new int[n];
        dp = new int[n];
    }

    public void setMap(int i, int value) {
        map[i] = value; // 0: 값
    }

    int getResult() {
        return result;
    }

    public void run() {
        dp[0] = 1; // 첫 번째 선택은 1

        for (int i = 1; i < n; i++) {
            for (int j = i - 1; j >= 0; j--) {
                if (map[j] < map[i]) { // 만약 현재 있는 상자의 값보다 적다면
                    dp[i] = Math.max(dp[j], dp[i]); // 그 적은 상자와 현재 dp를 비교하여 업데이트
                }
            }
            dp[i] += 1; // 자기 자신을 넣어야 하므로 +1
            result = Math.max(dp[i], result);
        }
    }
}

 

2. 풀이 중점 사항

 

현재 A라는 위치에 있는 상자를 기준으로 가장 많이 넣을 수 있는 경우를 판단하려면 

1 2 3 ~ A - 1까지 상자를 순회하며 몇 개의 상자를 넣을 수 있는지 판단해야 합니다. 

만약 1 5 3 7 이라는 상태가 주어졌고,

7에 넣을 수 있는 상자를 판단하려면 

1 , 5 넣거나 안넣거나, 만약 5가 있다면 3 X, 5가 없다면 3 O 이러한 식으로 복잡한 구성이 되어야 합니다.

 

이러한 문제를 해결하기 위한 방법이 DP를 활용하는 것입니다.

 

dp[0] = 1; // 첫 번째 선택은 1

먼저, 0번째 인덱스, 즉 첫 번째 원소를 상자에 넣어서 값이 최대가 되는 방법은 무조건 자기 자신을 넣는 것입니다.(자기 자신과 크기가 같은 상자가 있으므로)

 

for (int i = 1; i < n; i++) {
    for (int j = i - 1; j >= 0; j--) {
        if (map[j] < map[i]) { // 만약 현재 있는 상자의 값보다 적다면
            dp[i] = Math.max(dp[j], dp[i]); // 그 적은 상자와 현재 dp를 비교하여 업데이트
        }
    }
    dp[i] += 1; // 자기 자신을 넣어야 하므로 +1
    result = Math.max(dp[i], result);
}

map은 값을 저장하는 배열입니다. 1 5 3 7 을 map에 각각 인덱스에 맞게 넣는 것입니다.

dp는 각 인덱스에 최대 몇 개를 넣을 수 있는지를 판단하는 것입니다.

map과 dp를 함께 보면, map[i]에 있는 값을 가진 상자에 최대 몇 개의 상자를 넣을 수 있는지를 dp[i]에 기록하는 것입니다.

 

5 => index 1이므로,

if (map[0] < map[1]) :

    dp[i] = Math.max(dp[0], dp[1]) 

 

dp[i] += 1

 

이전 dp의 최대이면서, 자기 자신보다 값이 작은 상자를 골라 하나를 더해주는 로직을 완성할 수 있습니다.

 

이상으로 상자넣기 자바 풀이를 마치도록 하겠습니다. 감사합니다.!

안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.

이번 포스팅은 백준 DP문제 - 합분해 자바 풀이를 진행하도록 하겠습니다.

문제 출처: https://www.acmicpc.net/problem/2225

 

2225번: 합분해

첫째 줄에 답을 1,000,000,000으로 나눈 나머지를 출력한다.

www.acmicpc.net

 

1. 풀이 소스

 

import java.io.*;
import java.util.*;
import static java.lang.Integer.parseInt;

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());
        int n = parseInt(st.nextToken());
        int k = parseInt(st.nextToken());
        
        SumDivision sd = new SumDivision(n, k);
        sd.run();
    }
}

class SumDivision {
    
    static final int MOD = 1000000000;
    int n;
    int k;
    int[][] dp;

    SumDivision(int n, int k) {
        this.n = n;
        this.k = k;
        dp = new int[k + 1][n + 1]; // i 개로 총 합이 j을 만드는 것        
    }
    
    void run() {

        Arrays.fill(dp[1], 1); // i = 1, 즉 1개로 j의 값을 만드는 경우는 한 개
        for (int i = 1; i <= k; i++) dp[i][0] = 1; // 총 합이 0이 되도록 만드는 경우는 각 개수 별로 한 개씩 
        // ex) i = 1, 합이 0 ==> 0 (한 개 존재)
        // ex) i = 2, 합이 0 ==> 0 + 0 (한 개 존재)

        // dp[i][j] = dp[i - 1][0] + dp[i - 1][1] + .... + dp[i - 1][j];
        //   j
        // = sum(dp[a - 1][b]) 
        //   b -> 0 

        //   j - 1
        // = sum(dp[a - 1][b]) + dp[i - 1][j]
        //   b -> 0

        // = dp[i][j - 1] + dp[i - 1][j]
        for (int i = 2; i <= k; i++) {
            for (int j = 1; j <= n; j++) {
                dp[i][j] = dp[i][j - 1] + dp[i - 1][j];
                dp[i][j] %= MOD;
            }
        }

        System.out.println(dp[k][n]);
    }
}

 

2. 풀이 중점 사항

 

이번 문제는 점화식을 도출하는 과정이 많이 어려웠습니다.

정수 K개를 더해서 N까지의 합을 어떻게 만들어야 하는가가 핵심이었습니다.

 

하나의 예시를 살펴보겠습니다.

N을 만들기 위해서는 합이 0 ~ n 이 되도록 k - 1개를 사용하여야 합니다.

즉, N이 3이라면

k = 2일 때, preSum = 0, 1, 2, 3 이 완성되어야 합니다.

 

k = 3이라면,

preSum = 0     +      3 (현재)

preSum = 1     +      2 (현재)

preSum = 2     +      1 (현재)

preSum = 3     +      0 (현재)

 

를 구할 수 있습니다. 이를 수학적으로 표현하면 다음과 같습니다.

DP[a][b]가 a개를 사용하여 값이 b를 만드는 경우의 수라고 한다면,

 

DP[k][N] =  DP[k - 1][0] + DP[k - 1][1] +    ...   + DP[k - 1][N] 으로 표현할 수 있습니다.

하단에 참조 블로그의 블로거 분께서 점화식을 도출해주셨는데, 참고하면 다음과 같습니다.

 

 

시그마의 성질로 인해 마지막 항을 따로 분리하면, dp[K - 1][N]이 되고, 시그마를 정리하면 dp[k][N - 1]이 되므로 두 항을 더하면

하나의 점화식을 완성 시킬 수 있습니다.

 

dp[i][j] = dp[i][j - 1] + dp[i - 1][j];

 

이때, k = 1으로 N을 만드는 경우의 수는 자기 자신을 더하는 것밖에 없으므로 

dp[1][all] = 1 을 처리해주어야 합니다. 또한, k개로 0을 만드는 경우도 마찬가지로 1 개입니다.

 

ex) k = 1 -> 0

      k = 2 -> 0 + 0

 

따라서, 이 경우들을 제외하고는 위의 점화식을 적용시키면 문제를 해결할 수 있습니다.

이상으로 백준 합분해 자바 풀이를 마치도록 하겠습니다. 감사합니다.!

 

참고 자료: https://nahwasa.com/entry/%EB%B0%B1%EC%A4%80-2225-%EC%9E%90%EB%B0%94-%ED%95%A9%EB%B6%84%ED%95%B4-BOJ-2225-JAVA

 

안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.

이번 포스팅은 백준 DP문제 점프 자바 풀이를 진행하도록 하겠습니다.

문제 출처: https://www.acmicpc.net/problem/1890

 

1890번: 점프

첫째 줄에 게임 판의 크기 N (4 ≤ N ≤ 100)이 주어진다. 그 다음 N개 줄에는 각 칸에 적혀져 있는 수가 N개씩 주어진다. 칸에 적혀있는 수는 0보다 크거나 같고, 9보다 작거나 같은 정수이며, 가장

www.acmicpc.net

 

1. 풀이 소스

 

import java.util.*;
import java.io.*;
import static java.lang.Integer.parseInt;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = parseInt(br.readLine());
        
        Jump jump = new Jump(n);
        for (int row = 0; row < n; row++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            for (int col = 0; col < n; col++)
                jump.setMap(row, col, parseInt(st.nextToken()));
        }
        
        jump.run();
        System.out.println(jump.getPath());
    }
}

class Jump {
    int n;
    int[][] map; // 점프 값 저장
    long[][] dp; // 점프 후 dp 저장 2^63 - 1까지 저장 될 수 있으므로
    
    Jump (int n) {
        this.n = n;
        map = new int[n][n];
        dp = new long[n][n];
    }
    
    void setMap(int row, int col, int value) {
        map[row][col] = value; // 좌표에 맞는 값 설정하기
    }
    
    void run() {
        dp[0][0] = 1; // 초기 값 설정

        for (int row = 0; row < n; row++) {
            for (int col = 0; col < n; col++) {

                int d = map[row][col];
                if (d == 0) continue; // 만약 이동 거리가 0이라면 중복 저장을 막기 위함 dp[row][col + 0] += dp[row][col] 방지

                if (isValid(row, col + d)) { // 값만큼 열 점프
                    dp[row][col + d] += dp[row][col];
                }

                if (isValid(row + d, col)) { // 값만큼 행 점프
                    dp[row + d][col] += dp[row][col];
                }
            }
        }
    }
    
    boolean isValid(int row, int col) { // 유효한 인덱스
        return row >= 0 && row < n && col >= 0 && col < n;
    }

    long getPath() { // 마지막 0의 종료점 좌표
        return dp[n - 1][n - 1];
    }

}

 

2. 풀이 중점 사항

 

DP로 해결하므로 빅오(n^2) = (100 * 100)으로 문제를 해결할 수 있습니다.

중점 사항은 d == 0이 될 때, += 의 성질로 인해 같은 값이 중복 저장될 수 있습니다.

따라서, map[row][col] == 0이라면 해당 로직은 건너뛰어서, 값의 중복 저장을 막아야 합니다.

 

자세한 풀이는 주석화 하였습니다. 감사합니다.!

+ Recent posts