no image
알고리즘: 버블 정렬 (JAVA)
버블정렬 정렬 방식이 마치 물속 거품이 수면으로 올라가는 듯하다고 하여 붙여진 이름가장 단순하고 이해하기 쉬운 방식 중 하나이다시간복잡도 : O(n^2)    정렬 방식첫 번째 값과 두 번째 값 비교첫 번째 값이 크면 서로 위치 교환. 아니면 그대로!두 번째 값과 세 번째 값 비교하여 같은 작업 반복끝 값까지 같은 작업을 반복하면, 가장 큰 값이 가장 끝 배열 요소로 들어감같은 과정을 반복하면 오름차순 정렬이 완성    장단점장점: 구현이 간단하고 코드가 직관적이다!단점: 시간복잡도가 O(n^2)로, 배열 크기가 커지면 비효율적이다.      import java.util.Arrays;public class BubbleSort { public static void main(String[] args) { ..
2024.08.05
백준: 2164번 카드2 (자바, Java)
[백준] 2164번 카드2https://www.acmicpc.net/problem/2164   문제N장의 카드가 있다. 각각의 카드는 차례로 1부터 N까지의 번호가 붙어 있으며, 1번 카드가 제일 위에, N번 카드가 제일 아래인 상태로 순서대로 카드가 놓여 있다. 이제 다음과 같은 동작을 카드가 한 장 남을 때까지 반복하게 된다. 우선, 제일 위에 있는 카드를 바닥에 버린다. 그 다음, 제일 위에 있는 카드를 제일 아래에 있는 카드 밑으로 옮긴다. 예를 들어 N=4인 경우를 생각해 보자. 카드는 제일 위에서부터 1234 의 순서로 놓여있다. 1을 버리면 234가 남는다. 여기서 2를 제일 아래로 옮기면 342가 된다. 3을 버리면 42가 되고, 4를 밑으로 옮기면 24가 된다. 마지막으로 2를 버리고 나..
2024.05.28
백준: 1436번 영화감독 숌 (자바, Java)
[백준] 1436번 영화감독 숌https://www.acmicpc.net/problem/1436      문제숫자 6이 연속으로 3개 이상 포함되는 수 중 가장 작은 수는 666이다.그렇다면 숫자 6이 연속으로 3개 이상 포함되는 수 중  N번째로 작은 수를 구하는 프로그램을 작성하라. 입력 :첫째 줄에 N이 주어진다. N은 10,000보다 작거나 같은 자연수이다. 출력 : 첫째 줄에 N번째 영화의 제목에 들어간 수를 출력한다.  나의 답안처음에 문제를 잘못 읽었다. 6이 연속해서 3개 이상 포함되어야 하는데, 연속하지 않아도 3개 이상 포함되기만 하면 되는 줄 알고 charAt() 메서드를 사용해서 코드를 작성했다... 왜 안되나 계속 들여다 보다가 한참 뒤에 문제를 다시 읽고 원인을 발견했다 ㅋㅋㅋ ..
2024.05.25
백준: 1920번 수 찾기 (자바, Java)
[백준] 1920번 수 찾기https://www.acmicpc.net/problem/1920     문제육각형으로 이루어진 벌집이 있다. 중앙의 방 1부터 시작해서 이웃하는 방에 돌아가면서 1씩 증가하는 번호를 주소로 매길 수 있다. 숫자 N이 주어졌을 때, 벌집의 중앙 1에서 N번 방까지 최소 개수의 방을 지나서 갈 때 몇 개의 방을 지나가는지(시작과 끝을 포함하여)를 계산하는 프로그램을 작성하시오. 예를 들면, 13까지는 3개, 58까지는 5개를 지난다. 입력 :첫째 줄에 N(1 ≤ N ≤ 1,000,000,000)이 주어진다. 출력 :입력으로 주어진 방까지 최소 개수의 방을 지나서 갈 때 몇 개의 방을 지나는지 출력한다.  나의 답안 import java.util.*; public class Mai..
2024.05.24
백준: 1920번 수 찾기 (자바, Java)
[백준] 1920번 수 찾기https://www.acmicpc.net/problem/1920     문제N개의 정수 A[1], A[2], …, A[N]이 주어져 있을 때, 이 안에 X라는 정수가 존재하는지 알아내는 프로그램을 작성하라입력 :첫째 줄에 자연수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 N개의 정수 A[1], A[2], …, A[N]이 주어진다. 다음 줄에는 M(1 ≤ M ≤ 100,000)이 주어진다. 다음 줄에는 M개의 수들이 주어지는데, 이 수들이 A안에 존재하는지 알아내면 된다. 모든 정수의 범위는 -231 보다 크거나 같고 231보다 작다. 출력 :M개의 줄에 답을 출력. 존재하면 1, 존재하지 않으면 0을 출력 나의 답안 import java.util.*; publ..
2024.05.23
백준: 9012번 괄호 (자바, Java)
[백준] 9012번 괄호https://www.acmicpc.net/problem/9012     문제괄호 문자열(Parenthesis String, PS)은 두 개의 괄호 기호인 ‘(’ 와 ‘)’ 만으로 구성되어 있는 문자열이다. 그 중에서 괄호의 모양이 바르게 구성된 문자열을 올바른 괄호 문자열(Valid PS, VPS)이라고 부른다. 한 쌍의 괄호 기호로 된 “( )” 문자열은 기본 VPS 이라고 부른다. 만일 x 가 VPS 라면 이것을 하나의 괄호에 넣은 새로운 문자열 “(x)”도 VPS 가 된다. 그리고 두 VPS x 와 y를 접합(concatenation)시킨 새로운 문자열 xy도 VPS 가 된다. 예를 들어 “(())()”와 “((()))” 는 VPS 이지만 “(()(”, “(())()))” ..
2024.05.21
백준: 2525번 오븐 시계 (자바, Java)
[백준] 2525번 오븐 시계https://www.acmicpc.net/problem/2525    문제KOI 전자에서는 건강에 좋고 맛있는 훈제오리구이 요리를 간편하게 만드는 인공지능 오븐을 개발하려고 한다. 인공지능 오븐을 사용하는 방법은 적당한 양의 오리 훈제 재료를 인공지능 오븐에 넣으면 된다. 그러면 인공지능 오븐은 오븐구이가 끝나는 시간을 분 단위로 자동적으로 계산한다. 또한, KOI 전자의 인공지능 오븐 앞면에는 사용자에게 훈제오리구이 요리가 끝나는 시각을 알려 주는 디지털 시계가 있다. 훈제오리구이를 시작하는 시각과 오븐구이를 하는 데 필요한 시간이 분단위로 주어졌을 때, 오븐구이가 끝나는 시각을 계산하는 프로그램을 작성하시오. 입력 :첫째 줄에는 현재 시각이 나온다. 현재 시각은 시 A ..
2024.05.20
no image
백준: 2588번 곱셈 (자바, Java)
[백준] 2388번 곱셈https://www.acmicpc.net/problem/2588   문제(세 자리 수) × (세 자리 수)는 다음과 같은 과정을 통하여 이루어진다.(1)과 (2)위치에 들어갈 세 자리 자연수가 주어질 때 (3), (4), (5), (6)위치에 들어갈 값을 구하는 프로그램을 작성하라 입력 : 첫째 줄에 (1)의 위치에 들어갈 세 자리 자연수가, 둘째 줄에 (2)의 위치에 들어갈 세자리 자연수가 주어진다. 출력 : 첫째 줄부터 넷째 줄까지 차례대로 (3), (4), (5), (6)에 들어갈 값을 출력한다. 나의 답안 일단 (6)은 그냥 A*B 해주면 되고.. 100의 자리 수는 100으로 나눠주면 몫이 그것이고, 1의 자리는 A를 10으로 나눈 나머지로 하면 되겠네? 생각했다. 그리..
2024.05.19
프로그래머스: 도둑질 (자바, Java)
[프로그래머스] 도둑https://school.programmers.co.kr/learn/courses/30/lessons/42897   문제도둑이 어느 마을을 털 계획을 하고 있습니다. 이 마을의 모든 집들은 동그란 형태로 배치되어 있다.각 집들은 서로 인접한 집들과 방범장치가 연결되어 있어 인접한 두 집을 연속으로 털면 경보가 울린다. 각 집에 있는 돈이 담긴 배열 money가 주어질 때, 도둑이 훔칠 수 있는 돈의 최댓값을 return 하는 메서드를 작성하라.   나의 답안몇 시간을 헤맨 끝에 겨우 풀었다. 아래 코드들처럼 이것저것 시도해 보았으나 테스트케이스 및 효율성 검사에서 탈락.   첫 번째 코드(오답)import java.util.*;class Solution {    public int[..
2024.05.09
프로그래머스: 배열 조각하기 (자바, Java)
[프로그래머스] 배열 조각하기https://school.programmers.co.kr/learn/courses/30/lessons/181893  문제정수 배열 arr와 query가 주어지며, query를 순회하면서 다음 작업을 반복한다. 짝수 인덱스에서는 arr에서 query[i]번 인덱스를 제외하고 배열의 query[i]번 인덱스 뒷부분을 잘라서 버림홀수 인덱스에서는 arr에서 query[i]번 인덱스는 제외하고 배열의 query[i]번 인덱스 앞부분을 잘라서 버림위 작업을 마친 후 남은 arr의 부분 배열을 return 하는 solution 함수를 완성하라   나의 답안나는 문제에서 말하는 알고리즘대로 remove 메서드를 이용하여 짝수 인덱스에서는 뒷부분을 자르고,홀수 인덱스에서는 앞부분을 잘라내..
2024.05.08

알고리즘: 버블 정렬 (JAVA)

친환경 개발자
|2024. 8. 5. 16:21

버블정렬

 

  • 정렬 방식이 마치 물속 거품이 수면으로 올라가는 듯하다고 하여 붙여진 이름
  • 가장 단순하고 이해하기 쉬운 방식 중 하나이다
  • 시간복잡도 : O(n^2)

 

 

 

 

정렬 방식

  1. 첫 번째 값과 두 번째 값 비교
  2. 첫 번째 값이 크면 서로 위치 교환. 아니면 그대로!
  3. 두 번째 값과 세 번째 값 비교하여 같은 작업 반복
  4. 끝 값까지 같은 작업을 반복하면, 가장 큰 값이 가장 끝 배열 요소로 들어감
  5. 같은 과정을 반복하면 오름차순 정렬이 완성

 

 

 

 

장단점

  • 장점: 구현이 간단하고 코드가 직관적이다!
  • 단점: 시간복잡도가 O(n^2)로, 배열 크기가 커지면 비효율적이다.

 

 

 

 

 

 

import java.util.Arrays;

public class BubbleSort {

	public static void main(String[] args) {
		int[] arr = {22, 42, 31, 10, 35};
		
		// 버블 정렬
		for (int i=0; i<arr.length; i++) {
			for (int j=1; j<arr.length-i; j++) {
				if (arr[j-1] > arr[j]) {
					int tmp = arr[j-1];
					arr[j-1] = arr[j];
					arr[j] = tmp;
				}
			}
		}
		
		System.out.println(Arrays.toString(arr));
	}

}

 


[백준] 2164번 카드2

https://www.acmicpc.net/problem/2164

 

 

 

문제

N장의 카드가 있다. 각각의 카드는 차례로 1부터 N까지의 번호가 붙어 있으며, 1번 카드가 제일 위에, N번 카드가 제일 아래인 상태로 순서대로 카드가 놓여 있다.

이제 다음과 같은 동작을 카드가 한 장 남을 때까지 반복하게 된다. 우선, 제일 위에 있는 카드를 바닥에 버린다. 그 다음, 제일 위에 있는 카드를 제일 아래에 있는 카드 밑으로 옮긴다.

예를 들어 N=4인 경우를 생각해 보자. 카드는 제일 위에서부터 1234 의 순서로 놓여있다. 1을 버리면 234가 남는다. 여기서 2를 제일 아래로 옮기면 342가 된다. 3을 버리면 42가 되고, 4를 밑으로 옮기면 24가 된다. 마지막으로 2를 버리고 나면, 남는 카드는 4가 된다.

N이 주어졌을 때, 제일 마지막에 남게 되는 카드를 구하는 프로그램을 작성하시오.

 

입력 :

첫째 줄에 정수 N(1 ≤ N ≤ 500,000)이 주어진다.

출력 :

첫째 줄에 남게 되는 카드의 번호를 출력한다.

 

 

나의 답안

 

처음엔 다이나믹프로그래밍인가 하고

D(1) = 1

D(2) = 2

D(3) = 2

...

풀었으나, 일정 규칙이 보이질 않았다.

 

한참 헛다리 짚다가, 리스트를 사용해 직접 처리하는 코드를 작성했지만,

 

시간 초과로 실패.

 

알고보니 큐(Queue)를 이용해 푸는 문제였다.

 

<List를 사용한 코드>
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
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));
        int N = Integer.parseInt(br.readLine());

        List<Integer> list = new ArrayList<>();

        for (int i = 1; i <= N; i++) {
            list.add(i);
        }

        while (list.size() > 1) {
            list.remove(0);
            list.add(list.get(0));
            list.remove(0);
        }

        System.out.println(list.get(0));
    }
}

 

 

 

 

개선 사항

<Queue를 사용한 코드>
import java.io.*;
import java.util.LinkedList;
import java.util.Queue;

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

        Queue<Integer> queue = new LinkedList<>();

        for (int i = 1; i <= N; i++) {
            queue.add(i);
        }

        while (queue.size() > 1) {
            queue.poll();
            queue.add(queue.poll());
        }

        System.out.println(queue.peek());
    }
}
  • List에서의 remove(0)은 첫 번째 요소를 제거하고 나머지 요소를 1칸씩 당겨와야 하기 때문에
    O(N)의 시간복잡도를 가진다.
  • Queue의 poll()은 FIFO구조로 맨 앞의 것만 빼오면 알아서 정렬되기에 O(1)의 시간복잡도를 가진다.

 

큐는 거의 사용해보질 않아서 생각을 못했다.

 

가독성은 둘 다 비슷하지만, 

 

효율성 측면에서 큰 차이가 있었다.

 

각 타입의 성질을 잘 파악해서 활용해야겠다.


[백준] 1436번 영화감독 숌

https://www.acmicpc.net/problem/1436

 

 

 

 

 

 

문제

숫자 6이 연속으로 3개 이상 포함되는 수 중 가장 작은 수는 666이다.

그렇다면 숫자 6이 연속으로 3개 이상 포함되는 수 중  N번째로 작은 수를 구하는 프로그램을 작성하라.
 

입력 :

첫째 줄에 N이 주어진다. N은 10,000보다 작거나 같은 자연수이다.

출력 :

첫째 줄에 N번째 영화의 제목에 들어간 수를 출력한다.

 

 

나의 답안

처음에 문제를 잘못 읽었다.

 

6이 연속해서 3개 이상 포함되어야 하는데, 연속하지 않아도 3개 이상 포함되기만 하면

 

되는 줄 알고 charAt() 메서드를 사용해서 코드를 작성했다...

 

왜 안되나 계속 들여다 보다가 한참 뒤에 문제를 다시 읽고 원인을 발견했다 ㅋㅋㅋ

 

 

import java.util.*;

public class Main {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int count = 0;
        int i = 666;

        while (count < n) {
            String s = String.valueOf(i);
            if (s.contains("666")) count++;
            if (count == n) break;
            i++;
        }

        System.out.println(i);
    }
}

 

어떻게 하면 효율적으로 풀까 하다가 생각이 나지 않아 무식하게(?) 작성해봤는데 통과되었다.

 

count 변수를 이용해 몇 번째 숫자인지를 카운트했고,

 

666부터 시작해 수를 1씩 올려가며

 

contains()메서드를 이용해 해당 숫자에 6이 연속 3회 이상 포함되는지 확인했다.

 

 

 

개선 사항

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

public class Main {
    public static void main(String[] args) throws IOException{
        // Scanner에 비해 처리속도 높음
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));  
        int n = Integer.parseInt(br.readLine());

        int count = 1;
        int num = 666;

        while (count < n) {
            num++;    //조건문 말미에 넣는 대신 처음에 넣음으로써 break;문 제거
            if (Integer.toString(num).contains("666")) count++;
        }

        System.out.println(num);
    }
}
  • Scanner보다 BufferedReader를 통해 입력 받는 것이 처리 속도 면에서 유리
  • 문자열 변수를 별도로 생성하는 대신 Integer.toString(num)을 사용
  • while문 첫부분에 num++를 넣음으로써 불필요한 break문 제거

 

※브루트포스 알고리즘:

문제를 해결하기 위해 가능한 모든 경우의 수를 탐색하는 방법

규모가 작거나, 다른 최적화된 알고리즘을 사용하기 어려울 때 유용하다.

 

장점

1. 확실성: 모든 가능성을 탐색하기 때문에 100%의 정확도를 가짐

2. 단순성: 구현이 쉽고 직관적임

 

단점

1. 비효율성: 경우의 수가 많아질수록 시간이 오래 걸림

2. 고비용: 데이터소모가 매우 많아짐

 

브루트포스 알고리즘을 쓰더라도, 확실하게 정답이 아닌 것들은 최대한

제거해주는 코딩을 하는 것이 바람직할 것으로 보인다!


[백준] 1920번 수 찾기

https://www.acmicpc.net/problem/1920

 

 

 

 

 

문제

육각형으로 이루어진 벌집이 있다. 중앙의 방 1부터 시작해서 이웃하는 방에 돌아가면서 1씩 증가하는 번호를 주소로 매길 수 있다. 숫자 N이 주어졌을 때, 벌집의 중앙 1에서 N번 방까지 최소 개수의 방을 지나서 갈 때 몇 개의 방을 지나가는지(시작과 끝을 포함하여)를 계산하는 프로그램을 작성하시오. 예를 들면, 13까지는 3개, 58까지는 5개를 지난다.


입력 :

첫째 줄에 N(1 ≤ N ≤ 1,000,000,000)이 주어진다.

출력 :

입력으로 주어진 방까지 최소 개수의 방을 지나서 갈 때 몇 개의 방을 지나는지 출력한다.

 

 

나의 답안

 

import java.util.*;

public class Main {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();

        int count = 1;
        int a = 1;

        for (int i=0; i<N/6; i++) {
            a += 6*i;
            if(a >= N) {
                System.out.println(count);
                break;
            }
            count++;
        }
    }
}

 

계차수열처럼, 1칸씩 바깥으로 나갈수록 그전 값이랑 6*n만큼 차이가 나는 것을 알 수 있다.

 

따라서 N=1 이면 1칸, N=2~7이면 2칸, N=8~19이면 3칸 이런 방식.

 

for문을 사용해 a값을 6*i만큼 늘려가며 범위를 넓혔고,

 

count를 사용해 값을 반환하도록 작성했다.

 

다만, for문을 사용할 경우 i의 값을 N/6까지로 설정했는데,

 

break;를 사용하긴 했지만 불필요한 반복이 발생할 수도 있고

 

그렇게 보기 좋은 코드는 아닌 것 같다.

 

 

 

개선 사항

 

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        sc.close();
        
        int count = 1; 
        int range = 1; 
        
        while (N > range) {
            range += 6 * count;
            count++;
        }
        
        System.out.println(count);
    }
}
  • 변수를 a 대신 range로 바꿔 가독성을 높였다.
  • for문 대신 while문을 사용하여 불필요한 반복의 가능성을 제거했다.
  • 코드가 한결 간결해졌다.

 

계차수열을 어떻게 반복문에 표현할까를 생각보다 오래 고민했다.

 

바로바로 나오는 수준이 되어야 할텐데..

 

더 열공하자


[백준] 1920번 수 찾기

https://www.acmicpc.net/problem/1920

 

 

 

 

 

문제

N개의 정수 A[1], A[2], …, A[N]이 주어져 있을 때, 이 안에 X라는 정수가 존재하는지 알아내는 프로그램을 작성하라

입력 :

첫째 줄에 자연수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 N개의 정수 A[1], A[2], …, A[N]이 주어진다. 다음 줄에는 M(1 ≤ M ≤ 100,000)이 주어진다. 다음 줄에는 M개의 수들이 주어지는데, 이 수들이 A안에 존재하는지 알아내면 된다. 모든 정수의 범위는 -231 보다 크거나 같고 231보다 작다.

출력 :

M개의 줄에 답을 출력. 존재하면 1, 존재하지 않으면 0을 출력

 

나의 답안

 

import java.util.*;

public class Main {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        StringBuilder sb = new StringBuilder();

        for (int i=0; i<N; i++) {
            sb.append(sc.next());
        }
        String str = sb.toString();

        int M = sc.nextInt();
        for (int i=0; i<M; i++) {
            System.out.println(str.contains(sc.next())? 1 : 0);
        }
    }
}

 

처음엔 StringBuilder를 사용하여 답을 출력하는 코드를 작성했으나 시간초과되었다.

 

contains() 메서드를 사용했는데,

 

찾아보니 해당 메서드는 O(N*M)의 시간 복잡도(여기서는 O(5*5)를 가져 시간 초과를 유발한 것으로 보인다.

 

 

import java.util.*;

public class Main {

    static int[] A;
    public static int binarySearch(int start, int end, int target) {
        int mid = (start + end) / 2;
        if (mid > end || mid < start) return 0;
        if (A[mid] == target) return 1;
        if (A[mid] < target) return binarySearch(mid+1, end, target);
        if (A[mid] > target) return binarySearch(start, mid-1, target);
        return 0;
    }

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        A = new int[N];

        for (int i=0; i<N; i++) {
            A[i] = sc.nextInt();
        }
        Arrays.sort(A);

        int M = sc.nextInt();
        for (int i=0; i<M; i++) {
            System.out.println(binarySearch(0, N-1, sc.nextInt()));
        }
    }
}

 

이진탐색 방법을 사용하여 구했다.

입력을 A배열에 담고, 오름차순 정렬한 후,

입력받은 값과 A배열의 중간값을 비교하여 더 크면

중간값 이후의 값들과 비교, 작으면 중간값 이전의 값들과 비교해가며

탐색 영역을 반씩 줄여나가는 방법.

 

첫 번째 코드의 시간 복잡도는 contains메서드를 사용해 O(5*5)정도라고 하면,

이진탐색의 시간복잡도는 O(5*log5) 이기 때문에,

두번째 코드가 시간 초과를 면할 수 있던 것으로 보인다.

 

개선 사항

 

import java.util.*;

public class Main {

    static int[] A;

    public static int binarySearch(int start, int end, int target) {

        // while문으로 가독성 UP
        while (start <=end) {
            int mid = (start + end) / 2;
            if (A[mid] == target) return 1;
            if (A[mid] < target) start = mid + 1;
            else end = mid - 1;
        }
        return 0;
    }

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        A = new int[N];

        for (int i=0; i<N; i++) {
            A[i] = sc.nextInt();
        }
        Arrays.sort(A);

        int M = sc.nextInt();
        for (int i=0; i<M; i++) {
            System.out.println(binarySearch(0, N-1, sc.nextInt()));
        }
    }
}
  • 재귀호출 대신 while 조건문을 사용하여 가독성이 좋아진 듯하다.
  • 성능 면에서는 차이가 크게 없을 것으로 보임

 

Arrays.sort를 사용하면 무조건 비효율적일 것이다 생각했는데,

오히려 contains메서드를 사용한 코드가 더 비효율적일 수 있다는 것을 깨달았던 문제이다.


[백준] 9012번 괄호

https://www.acmicpc.net/problem/9012

 

 

 

 

 

문제

괄호 문자열(Parenthesis String, PS)은 두 개의 괄호 기호인 ‘(’ 와 ‘)’ 만으로 구성되어 있는 문자열이다. 그 중에서 괄호의 모양이 바르게 구성된 문자열을 올바른 괄호 문자열(Valid PS, VPS)이라고 부른다. 한 쌍의 괄호 기호로 된 “( )” 문자열은 기본 VPS 이라고 부른다. 만일 x 가 VPS 라면 이것을 하나의 괄호에 넣은 새로운 문자열 “(x)”도 VPS 가 된다. 그리고 두 VPS x 와 y를 접합(concatenation)시킨 새로운 문자열 xy도 VPS 가 된다. 예를 들어 “(())()”와 “((()))” 는 VPS 이지만 “(()(”, “(())()))” , 그리고 “(()” 는 모두 VPS 가 아닌 문자열이다. 

여러분은 입력으로 주어진 괄호 문자열이 VPS 인지 아닌지를 판단해서 그 결과를 YES 와 NO 로 나타내어야 한다. 

입력 :

입력 데이터는 표준 입력을 사용한다. 입력은 T개의 테스트 데이터로 주어진다. 입력의 첫 번째 줄에는 입력 데이터의 수를 나타내는 정수 T가 주어진다. 각 테스트 데이터의 첫째 줄에는 괄호 문자열이 한 줄에 주어진다. 하나의 괄호 문자열의 길이는 2 이상 50 이하이다. 

출력 :

출력은 표준 출력을 사용한다. 만일 입력 괄호 문자열이 올바른 괄호 문자열(VPS)이면 “YES”, 아니면 “NO”를 한 줄에 하나씩 차례대로 출력해야 한다. 

 

나의 답안

 

1. counter=0 변수 선언

2. 맨 앞글자부터 '(' 일 때 counter + 1      ')' 일 때 counter - 1 해준다.

3. counter값이 0이면 YES, 아니면 NO 출력

4. 도중에 counter값이 음수가 될 경우 반복문 종료 후 NO 출력

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());    // 입력 개수 입력

        for (int i=0; i<N; i++) {        
            String str = br.readLine(); 
            int counter = 0;
            for (int j=0; j<str.length(); j++){
                if (str.charAt(j) == '(') {
                    counter += 1;
                } else {
                    counter -= 1;
                    if (counter < 0) {
                        break;
                    }
                }
            }
            System.out.println(counter==0 ? "YES" : "NO");
        }
    }
}

 

삼항연산자를 사용하여 작성해봤다.

 

짧지만 가독성은 좋지 않다.

 

 

다른 답안

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

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int N = Integer.parseInt(br.readLine());
        Stack<Character> stack = new Stack<>();

        for(int i=0; i<N; i++) {
            String str = br.readLine();
            int length = str.length();

            for(int j=0; j<length; j++) {
                char ch = str.charAt(j);

                if(ch == '(') {
                    stack.push(ch);
                }
                else {
                    int size = stack.size();
                    if(size == 0) {
                        stack.push(ch);
                        break;
                    }
                    else {
                        stack.pop();
                    }
                }
            }

            if(stack.isEmpty()) {
                System.out.println("YES");
            }
            else {
                System.out.println("NO");
            }

            stack.clear();
        }
    }
}
  • stack구조를 사용해 좀 더 간단하게 구현했다.
  • '(' 일 때 stack.push()를, ')'일 때 stack.pop()을 사용
  • ')'일 때 stack에 요소가 없으면 반복문 빠져나와 NO 출력

stack 구조를 사용해야겠다는 생각을 하지 못했다.

stack을 사용하기에 아주 좋은 문제였던 것 같다...

 

다만, counter변수를 사용하는 것이  메모리 사용량이 더 적고 연산이 간단하며,

stack을 사용하는 것은  코드의 길이를 늘리고 복잡성을 증가시킬 수 있으므로 주의


[백준] 2525번 오븐 시계

https://www.acmicpc.net/problem/2525

 

 

 

 

문제

KOI 전자에서는 건강에 좋고 맛있는 훈제오리구이 요리를 간편하게 만드는 인공지능 오븐을 개발하려고 한다. 인공지능 오븐을 사용하는 방법은 적당한 양의 오리 훈제 재료를 인공지능 오븐에 넣으면 된다. 그러면 인공지능 오븐은 오븐구이가 끝나는 시간을 분 단위로 자동적으로 계산한다.

또한, KOI 전자의 인공지능 오븐 앞면에는 사용자에게 훈제오리구이 요리가 끝나는 시각을 알려 주는 디지털 시계가 있다.

훈제오리구이를 시작하는 시각과 오븐구이를 하는 데 필요한 시간이 분단위로 주어졌을 때, 오븐구이가 끝나는 시각을 계산하는 프로그램을 작성하시오.


입력 :

첫째 줄에는 현재 시각이 나온다. 현재 시각은 시 A (0 ≤ A ≤ 23) 와 분 B (0 ≤ B ≤ 59)가 정수로 빈칸을 사이에 두고 순서대로 주어진다. 두 번째 줄에는 요리하는 데 필요한 시간 C (0 ≤ C ≤ 1,000)가 분 단위로 주어진다.

출력 :

첫째 줄에 종료되는 시각의 시와 분을 공백을 사이에 두고 출력한다. (단, 시는 0부터 23까지의 정수, 분은 0부터 59까지의 정수이다. 디지털 시계는 23시 59분에서 1분이 지나면 0시 0분이 된다.)

 

 

나의 답안

 

분이 60분이 넘을 때, 60으로 나눈 몫만큼 '시' 부분을 높여주되,

'시' 부분이 24이상일 경우에는 0부터 표현해줘야 한다.

 

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt();
        int b = sc.nextInt();
        int c = sc.nextInt();

        System.out.println(a+(b+c)/60 > 23 ? a+(b+c)/60-24 + " " + (b+c)%60 : a+(b+c)/60 + " " + (b+c)%60);
    }
}

 

삼항연산자를 사용하여 작성해봤다.

 

짧지만 가독성은 좋지 않다.

 

 

모범 답안

import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);

int A = sc.nextInt();
int B = sc.nextInt();
int C = sc.nextInt();

B += C;

A = (A + B/60)%24;
B = B%60;

System.out.println(A+" "+B);
}
}
  • B에 C를 더한 값으로 B에 다시 할당함
  • 나는 if문을 사용해 '시'가 24 이상일 경우 24를 빼도록 작성했지만,
    해당 답안에서는 if문 사용 없이 답을 도출할 수 있도록 작성되었다.

 

B에 바로 C값을 더해주는 부분과 A값을 if문 없이 표현했다는 점이 인상 깊다.

내 코드에 비해 가독성과 효율성 측면에서 더 좋은 코드라는 생각이 든다.


[백준] 2388번 곱셈

https://www.acmicpc.net/problem/2588

 

 

 

문제

(세 자리 수) × (세 자리 수)는 다음과 같은 과정을 통하여 이루어진다.

(1)과 (2)위치에 들어갈 세 자리 자연수가 주어질 때 (3), (4), (5), (6)위치에 들어갈 값을 구하는 프로그램을 작성하라

 

입력 :

첫째 줄에 (1)의 위치에 들어갈 세 자리 자연수가, 둘째 줄에 (2)의 위치에 들어갈 세자리 자연수가 주어진다.

출력 :

첫째 줄부터 넷째 줄까지 차례대로 (3), (4), (5), (6)에 들어갈 값을 출력한다.

 

나의 답안

 

일단 (6)은 그냥 A*B 해주면 되고..

 

100의 자리 수는 100으로 나눠주면 몫이 그것이고,

 

1의 자리는 A를 10으로 나눈 나머지로 하면 되겠네? 생각했다.

 

그리고 10의 자리가 조금 고민이었다.

 

그러다 결국 10으로 나누면, 100의 자리와 10의 자리만 남을 것이고, 거기에 10을 나눈 나머지가

 

10의 자리 수가 되겠다는 생각을 하고 코드를 작성했다.

 

 

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int A = sc.nextInt();
        int B = sc.nextInt();


        System.out.println(A*(B%10));
        System.out.println(A*((B/10)%10));
        System.out.println(A*(B/100));
        System.out.println(A*B);
    }
}

 

 

모범 답안

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
         int a = sc.nextInt();
         int b = sc.nextInt();

         int c = (b % 10) * a;
         int d = ((b % 100) / 10) * a;
         int e = (b / 100) * a;
         System.out.println(c);
         System.out.println(d);
         System.out.println(e);
         System.out.println(a * b);
    }
}

 

 

  • 1의 자리 수와 100의 자리는 내 답안과 동일.
  • 10의 자리는 100을 나눈 나머지에 10을 나눈 몫으로 추출

 

[프로그래머스] 도둑

https://school.programmers.co.kr/learn/courses/30/lessons/42897

 

 

문제

도둑이 어느 마을을 털 계획을 하고 있습니다. 이 마을의 모든 집들은 동그란 형태로 배치되어 있다.

각 집들은 서로 인접한 집들과 방범장치가 연결되어 있어 인접한 두 집을 연속으로 털면 경보가 울린다.

각 집에 있는 돈이 담긴 배열 money가 주어질 때, 도둑이 훔칠 수 있는 돈의 최댓값을 return 하는 메서드를 작성하라.

 

 

 

나의 답안

몇 시간을 헤맨 끝에 겨우 풀었다.

 

아래 코드들처럼 이것저것 시도해 보았으나 테스트케이스 및 효율성 검사에서 탈락.

 

 

 

첫 번째 코드(오답)

import java.util.*;

class Solution {
    public int[] solution(int[] arr, int[] query) {
        int[] dp = new int[money.length];

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

        for (int i=2; i<money.length; i++) {
            dp[i] = Math.max(money[i]+dp[i-2], dp[i-1]);
        }

        int answer = dp[dp.length-1];
        return answer;
    }
}
  • n번째 집을 터는 경우와 털지 않는 경우 둘로 나누어 더 큰 값을 dp(n)의 값으로 결정하는 방법.
    집이 원형으로 배치되어 있다는 것을 망각하고 풀었다.
    따라서 맨 앞의 값과 뒤의 값을 둘 다 터는 경우가 발생하므로 오답이다.

 

두 번째 코드(오답)

class Solution {
    public int solution(int[] money) {
        int evenSum = 0, oddSum = 0;
        for (int i = 0; i < money.length; i++) {
            if (i % 2 == 0) {
                evenSum += money[i];
                System.out.println("evenSum :"+evenSum);
            } else {
                oddSum += money[i];
                System.out.println("oddSum :"+oddSum);
            }
        }

        if (money.length % 2 == 1) {
            evenSum -= Math.min(money[0], money[money.length - 1]);
        }

        return Math.max(evenSum, oddSum);
}
  • 한 칸 건너뛰면서 더하는 거니까, 홀수 인덱스 값들의 합과 짝수 인덱스 값들의 합을 비교하면 되겠네? 생각하고 작성.
    꼭 한 칸씩만 건너뛰면서 더해야 최대가 되는 것은 아니다. 예를 들어,
    [1000, 1, 1, 1000, 2] 의 경우 2000이 최댓값이지만, 위의 코드대로 수행하면 1003이 반환될 것이다.

 

 

세 번째 코드(오답)

class Solution {
    public int solution(int[] money) {
        int[] dp1 = new int[money.length];
        int[] dp2 = new int[money.length];

        dp1[0] = money[0];
        dp1[1] = Math.max(money[0], money[1]);
        dp2[money.length-1] = money [money.length-1];
        dp2[money.length-2] = Math.max(money[0], money[1]);

        int idx2 = money.length-3;
        for (int i=2; i<money.length-1; i++) {
            dp1[i] = Math.max(money[i]+dp![i-2], dp1[i-1]);
            dp2[idx2] = Math.max(money[idx2]+dp2[idx_r+2], dp2[idx_r+1]);
            
            idx2--;
        }

        return Math.max(dp1[money.length-2], dp2[1]);
}
  • 맨 앞과 뒤를 연속해서 털지 않도록  dp1과 dp2로 나누어
    dp1은 앞에서부터 n-1번째 값까지, dp2는 n번째 값부터 2번째 값까지 최댓값을 구하는 코드를 작성했다.
    이건 사실 왜 오답인지 아직도 모르겠다. 예외 케이스가 무엇인지 알려주실 선배님들 부탁드립니다..

 

 

모범 답안

  1. 원형으로 되어있으므로 맨 앞집을 터는 경우와 털지 않는 경우로 나누어 dp1, dp2로 각각 분류
  2. dp1에서는 맨 앞집부터 맨 끝집 직전 집까지 털 경우의 최대값을 구함
  3. dp2에서는 두 번째 집부터 맨 끝집까지 털 경우의 최대값을 구함
  4. dp1, dp2 중 최대값을 반환
class Solution {
    public int solution(int[] money) {
        int[] dp1 = new int[money.length];
        int[] dp2 = new int[money.length];

        dp1[0] = money[0];
        dp1[1] = Math.max(money[0], money[1]);
        dp2[0] = money[1];
        dp2[1] = Math.max(money[1], money[2]);

        for (int i = 2; i < money.length - 1; i++) {
            dp1[i] = Math.max(money[i] + dp1[i - 2], dp1[i - 1]);
            dp2[i] = Math.max(money[i+1] + dp2[i - 2], dp2[i - 1]);
        }

        return Math.max(dp1[money.length - 2], dp2[money.length - 2]);
    }
}

 

 

 

저녁시간 내내 이것만 고민하다가 끝났지만, 어느 정도 모범답안에 가까운 답을 도출해서 아주 뿌듯했다..

 

점점 발전하고 있는 것이 보인다!

[프로그래머스] 배열 조각하기

https://school.programmers.co.kr/learn/courses/30/lessons/181893

 

 

문제

정수 배열 arr와 query가 주어지며, query를 순회하면서 다음 작업을 반복한다.

  • 짝수 인덱스에서는 arr에서 query[i]번 인덱스를 제외하고 배열의 query[i]번 인덱스 뒷부분을 잘라서 버림
  • 홀수 인덱스에서는 arr에서 query[i]번 인덱스는 제외하고 배열의 query[i]번 인덱스 앞부분을 잘라서 버림


위 작업을 마친 후 남은 arr의 부분 배열을 return 하는 solution 함수를 완성하라

 

 

 

나의 답안

나는 문제에서 말하는 알고리즘대로 remove 메서드를 이용하여

 

짝수 인덱스에서는 뒷부분을 자르고,

홀수 인덱스에서는 앞부분을 잘라내고

 

배열로 변환하도록 코드를 작성했다.

 

import java.util.*;

class Solution {
    public int[] solution(int[] arr, int[] query) {
        ArrayList <Integer> list = new ArrayList<>();

        for(int i=0; i<arr.length; i++)
            list.add(arr[i]);

        for(int i=0; i<query.length; i++) {
            if(i%2 == 0) {
                int size = list.size() - query[i]-1;
                for(int j=0; j<size; j++)
                    list.remove(query[i]+1);
            }
            else {
                for(int j=0; j<query[i]; j++)
                    list.remove(0);
            }
        }

        int[] answer = new int [list.size()];
        for(int i=0; i<list.size(); i++)
            answer[i] = list.get(i);
        return answer;
    }
}

 

 

 

모범 답안

  1. 실제로 배열 내 값을 제거하는 과정을 생략
  2. start, end 변수를 이용하여 최종적으로 불러올 인덱스 범위를 조절
  3. copyOfRange변수를 사용하여 변수 불러옴
import java.util.*;

class Solution {
    public int[] solution(int[] arr, int[] query) {
        int start = 0;
        int end = arr.length - 1;
        for (int i = 0; i < query.length; i++) {
            if (i % 2 == 0) {
                end = start + query[i] - 1;
            } else {
                start += query[i];
            }
        }

        return Arrays.copyOfRange(arr, start, end + 2);
    }
}

 

 

 

시작점과 끝점만 조정해가며 값을 불러올 수 있다는 생각을 못했다.

 

순진하게(?) 문제에서 얘기하는 대로 코드를 짰는데,

 

훨씬 효율적인 처리가 가능한 방법이 있었다.

 

더 효율적인 방법이 있다는 것을 항상 생각하자!

 

 

 

 

 

 

복습

 

Arrays.copyOfRange(arr, 시작인덱스, 끝인덱스):

arr배열의 시작인덱스부터 끝인덱스(미포함)의 앞까지 복사하여 반환