문제

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

KOI 통신연구소는 레이저를 이용한 새로운 비밀 통신 시스템 개발을 위한 실험을 하고 있다. 실험을 위하여 일직선 위에 N개의 높이가 서로 다른 탑을 수평 직선의 왼쪽부터 오른쪽 방향으로 차례로 세우고, 각 탑의 꼭대기에 레이저 송신기를 설치하였다. 모든 탑의 레이저 송신기는 레이저 신호를 지표면과 평행하게 수평 직선의 왼쪽 방향으로 발사하고, 탑의 기둥 모두에는 레이저 신호를 수신하는 장치가 설치되어 있다. 하나의 탑에서 발사된 레이저 신호는 가장 먼저 만나는 단 하나의 탑에서만 수신이 가능하다.

예를 들어 높이가 6, 9, 5, 7, 4인 다섯 개의 탑이 수평 직선에 일렬로 서 있고, 모든 탑에서는 주어진 탑 순서의 반대 방향(왼쪽 방향)으로 동시에 레이저 신호를 발사한다고 하자. 그러면, 높이가 4인 다섯 번째 탑에서 발사한 레이저 신호는 높이가 7인 네 번째 탑이 수신을 하고, 높이가 7인 네 번째 탑의 신호는 높이가 9인 두 번째 탑이, 높이가 5인 세 번째 탑의 신호도 높이가 9인 두 번째 탑이 수신을 한다. 높이가 9인 두 번째 탑과 높이가 6인 첫 번째 탑이 보낸 레이저 신호는 어떤 탑에서도 수신을 하지 못한다.

탑들의 개수 N과 탑들의 높이가 주어질 때, 각각의 탑에서 발사한 레이저 신호를 어느 탑에서 수신하는지를 알아내는 프로그램을 작성하라.

입력

첫째 줄에 탑의 수를 나타내는 정수 N이 주어진다. N은 1 이상 500,000 이하이다. 둘째 줄에는 N개의 탑들의 높이가 직선상에 놓인 순서대로 하나의 빈칸을 사이에 두고 주어진다. 탑들의 높이는 1 이상 100,000,000 이하의 정수이다.

 

출력

첫째 줄에 주어진 탑들의 순서대로 각각의 탑들에서 발사한 레이저 신호를 수신한 탑들의 번호를 하나의 빈칸을 사이에 두고 출력한다. 만약 레이저 신호를 수신하는 탑이 존재하지 않으면 0을 출력한다.


코드

package barkingDog.stack;

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

public class Main {
    public static class Node{
        int idx;
        int value;

        public Node(int idx, int value){
            this.idx = idx;
            this.value = value;
        }
    }

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

        int N = Integer.parseInt(br.readLine());

        StringTokenizer st = new StringTokenizer(br.readLine());

        Stack<Node> stack = new Stack<>();

        StringBuilder sb = new StringBuilder();

        for(int i = 1; i <= N; i++){
            int v = Integer.parseInt(st.nextToken());

            while(true){
                // 스택에 아무 것도 없으면
                if(stack.isEmpty()){
                    // 현재 값을 스택에 넣기
                    // 내 앞에 값이 없으니(큰 값이 없으니) 0 넣기
                    stack.push(new Node(i, v));
                    sb.append("0 ");
                    break;
                }

                // 스택에 값이 있으면
                Node node = stack.peek();

                // 스택에 있는 값보다 현재 값이 작으면
                if(node.value < v){
                    // 스택에 있는 값 뽑기
                    // 나보다 큰 값이 있거나 스택이 빌 때까지
                    // 계속 뽑게 됨
                    stack.pop();
                }
                // 스택에 있는 값보다 현재 값이 크거나 같으면
                // 스택에 있는 값이 신호를 받을 것임!
                else{
                    // 스택에 있는 값의 인덱스를 정답에 추가
                    // 스택에 현재 값 추가하기
                    sb.append(node.idx).append(" ");
                    stack.push(new Node(i, v));
                    break;
                }


            }
        }

        System.out.println(sb.toString());
    }
}

 

현재 탑보다 높은 탑 중 가장 왼쪽으로 현재 탑과 가까운 탑이 신호를 수신한다. 방향도 한 방향이며, 나랑 가장 가까운 값을 확인하는 것이기 때문에 스택을 사용해 문제를 풀었다. 결과는 stringbuilder에 중간 중간 추가하였다.

주석에 자세하게 작성해놓았지만, 스택이 비어있을 때는 현재 탑의 높이를 스택에 넣고, 내 앞에 값이 없다는 것은 나보다 큰 값이 없다는 얘기이므로 0도 같이 sb에 추가해주었다. 스택에 값이 있을 때는 스택의 가장 상단에 있는 값과 현재 값을 비교해 현재 값보다 큰 값이 나올 때까지 계속 스택에서 뽑는다. 현재 값보다 큰 값이 스택에 최상단에 있게 되면 그 값을 stringbuilder에 추가하고 스택에 현재 값을 넣는다.

이때, 현재 탑의 높이보다 큰 값이 나올 때까지 계속 뽑아도 되는 이유가 궁금할 수 있는데, 현재 값의 오른쪽에 있는 탑들 중 현재 값보다 낮은 높이인 탑의 입장에서는 현재 탑의 왼쪽 탑 중 현재 탑보다 높은 탑이 없다면 어차피 현재 탑이 신호를 수신할 것이고, 현재 탑의 높이는  나중에 스택에 넣기 때문에 상관없다. 또한, 현재 탑의 오른쪽에 있는 탑들 중 현재 값보다 높은 높이인 탑의 입장에서는 어차피 현재 탑의 높이가 자신의 높이보다 낮기 때문에 적어도 현재 탑의 높이보다 낮은 탑들은 일단 수신을 받을 수 없음이 확실하므로 스택에서 제거되어도 상관없다.

 

++ 추가로 원래 입력을 적게 받는다고 생각해 Scanner를 사용하였는데, 메모리 초과가 나서 BufferedReader로 변경하여 해결했다. 문제를 읽어보니 N이 500,000까지 돼 탑을 500,000개 입력 받을 수도 있었다... Scanner와 BufferedReader의 메모리 효율이 다른 이유는 아래에 따로 정리하겠다.


(수정 예정! Scanner vs BufferedReader 의 메모리 효율)

728x90

문제

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

지민이는 N개의 원소를 포함하고 있는 양방향 순환 큐를 가지고 있다. 지민이는 이 큐에서 몇 개의 원소를 뽑아내려고 한다.

지민이는 이 큐에서 다음과 같은 3가지 연산을 수행할 수 있다.

  1. 첫 번째 원소를 뽑아낸다. 이 연산을 수행하면, 원래 큐의 원소가 a1, ..., ak이었던 것이 a2, ..., ak와 같이 된다.
  2. 왼쪽으로 한 칸 이동시킨다. 이 연산을 수행하면, a1, ..., ak가 a2, ..., ak, a1이 된다.
  3. 오른쪽으로 한 칸 이동시킨다. 이 연산을 수행하면, a1, ..., ak가 ak, a1, ..., ak-1이 된다.

큐에 처음에 포함되어 있던 수 N이 주어진다. 그리고 지민이가 뽑아내려고 하는 원소의 위치가 주어진다. (이 위치는 가장 처음 큐에서의 위치이다.) 이때, 그 원소를 주어진 순서대로 뽑아내는데 드는 2번, 3번 연산의 최솟값을 출력하는 프로그램을 작성하시오.

 

입력

첫째 줄에 큐의 크기 N과 뽑아내려고 하는 수의 개수 M이 주어진다. N은 50보다 작거나 같은 자연수이고, M은 N보다 작거나 같은 자연수이다. 둘째 줄에는 지민이가 뽑아내려고 하는 수의 위치가 순서대로 주어진다. 위치는 1보다 크거나 같고, N보다 작거나 같은 자연수이다.

 

출력

첫째 줄에 문제의 정답을 출력한다.


코드

package barkingDog.deque;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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());

        int minCnt = 0;

        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());

        st = new StringTokenizer(br.readLine());


        Deque<Integer> deque = new ArrayDeque<>();

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

        for(int i = 0; i < M; i++){
            String target = st.nextToken();
            int targetIdx = 0;

            Iterator iterator = deque.iterator();

            while(iterator.hasNext()){
                if(iterator.next().toString().equals(target)){
                    break;
                }
                targetIdx++;
            }

            if(targetIdx < deque.size() - targetIdx){
                // 왼쪽으로 회전시키기
                while(deque.peekFirst() != Integer.parseInt(target)){
                    deque.add(deque.poll());
                    minCnt++;
                }

            }else{
                // 오른쪽으로 회전시키기
                while(deque.peekFirst() != Integer.parseInt(target)){
                    deque.addFirst(deque.pollLast());
                    minCnt++;
                }
            }

            // 맨 앞에 있는 target 뽑기
            deque.pollFirst();
        }

        System.out.println(minCnt);
    }
}

문제에서 뽑고 싶은 수를 제일 앞으로 위치 시켜야 1번 연산을 통해 원하는 수를 뽑을 수 있다. 또한, 최소로 2번과 3번 연산을 사용하기 위해서는 ① 현재 뽑고 싶은 target 원소의 위치를 알아내고 ②왼쪽과 오른쪽 중에서 최소로 회전할 수 있는 방향을 고른다.

 

① 현재 뽑고 싶은 target 원소의 인덱스를 알아내기

Iterator를 활용하였고, target 값과 같은 원소를 발견하면 break 해서 target의 인덱스를 알아냈다.

 

② 왼쪽 회전 vs 오른쪽 회전

왼쪽 회전을 해서 특정 원소를 맨 앞으로 이동시키는 데 걸리는 횟수는 target의 index와 같고, 오른쪽 회전을 해서 특정 원소를 맨 앞으로 이동시키는 데 걸리는 횟수는 전체 원소 개수 - target의 index와 같다.

둘 중에 더 적은 횟수인 방식을 고르고, 실제로 회전을 시켜주면서 개수도 세고 원소를 빼고 넣는 연산도 같이 해주었다. 물론 회전 횟수는 이미 구해놨으나, 원소를 빼고 넣는 연산은 필수적이기 때문에 하는 김에 같이 횟수도 정석으로 구해주었다.

 

728x90

'Algorithm > 백준' 카테고리의 다른 글

[백준][Java] 2493. 탑  (0) 2024.08.13
[백준][Java] 2178. 미로 탐색  (0) 2024.02.11
[백준][Java] 1920. 수 찾기  (0) 2024.02.11
[백준][Java] 2630. 색종이 만들기  (0) 2024.02.06
[백준][Java] 14888. 연산자 끼워넣기  (0) 2024.02.05

문제

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

N×M크기의 배열로 표현되는 미로가 있다.

1 0 1 1 1 1
1 0 1 0 1 0
1 0 1 0 1 1
1 1 1 0 1 1

미로에서 1은 이동할 수 있는 칸을 나타내고, 0은 이동할 수 없는 칸을 나타낸다. 이러한 미로가 주어졌을 때, (1, 1)에서 출발하여 (N, M)의 위치로 이동할 때 지나야 하는 최소의 칸 수를 구하는 프로그램을 작성하시오. 한 칸에서 다른 칸으로 이동할 때, 서로 인접한 칸으로만 이동할 수 있다.

위의 예에서는 15칸을 지나야 (N, M)의 위치로 이동할 수 있다. 칸을 셀 때에는 시작 위치와 도착 위치도 포함한다.

 

입력

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

 

출력

첫째 줄에 지나야 하는 최소의 칸 수를 출력한다. 항상 도착위치로 이동할 수 있는 경우만 입력으로 주어진다.


코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
import java.util.StringTokenizer;

public class Main {
    static class Node {
        int x;
        int y;
        int cnt; // 현재까지 이동한 칸 수

        public Node(int x, int y, int cnt){
            this.x =  x;
            this.y = y;
            this.cnt = cnt;
        }
    }

    static int[][] maze;
    static boolean[][] visited;
    static int N;
    static int M;
    static int moveCnt;
    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());

        maze = new int[N+1][M+1];
        visited = new boolean[N+1][M+1];

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

            for(int j = 0; j < M; j++){
                maze[i+1][j+1] = str.charAt(j) - '0';
            }
        }

        bfs();

        System.out.println(moveCnt);

    }


    static void bfs(){
        // 상 하 좌 우
        int[] dx = new int[]{0,0,-1,1};
        int[] dy = new int[]{-1,1,0,0};

        Queue<Node> queue = new LinkedList<>();
        queue.add(new Node(1,1,1));
        visited[1][1] = true;

        while(!queue.isEmpty()){
            Node node = queue.poll();

            for(int i = 0; i < 4; i++){
                int nx = node.x + dx[i];
                int ny = node.y + dy[i];

                if(checkBoundary(nx, ny)){
                    if(nx == N && ny == M) {
                        moveCnt = node.cnt + 1;
                        return;
                    }
                    visited[nx][ny] = true;
                    queue.add(new Node(nx, ny, node.cnt + 1));
                }
            }
        }
    }

    static boolean checkBoundary(int nx, int ny){
        return nx >= 1 && nx <= N && ny >= 1 && ny <= M && maze[nx][ny] == 1 && !visited[nx][ny];
    }
}

BFS를 이용해서 풀었다. visited 배열을 이용해서 방문 여부를 저장하고, queue에 출발하는 칸인 (1,1)을 넣었다. 이때, Node class를 생성하여 넣어주었는데, x좌표, y좌표, 현재까지 이동한 총 cnt를 저장하고 있는 객체이다. queue가 빌 때까지 queue에서 객체를 하나 뽑고, 해당 객체의 상, 하, 좌, 우에 있는 값을 큐에 넣어준다. 이동할 때는 미로의 경계값을 넘지 않는지와 처음 방문하는 곳인지를 확인하고, 큐에 Node를 넣을 때는 cnt를 하나씩 올려서 넣어준다. 원하는 값은 x가 N이고, y가 M일 때이기 때문에, 다음에 방문할 x와 y인 nx, ny 값을 확인하여 nx가 N이고 ny가 M일 때 최소 이동 칸 수를 저장하는 moveCnt에 값을 저장해주고 return 한다.

728x90

'Algorithm > 백준' 카테고리의 다른 글

[백준][Java] 2493. 탑  (0) 2024.08.13
[백준][Java] 1021. 회전하는 큐  (0) 2024.08.10
[백준][Java] 1920. 수 찾기  (0) 2024.02.11
[백준][Java] 2630. 색종이 만들기  (0) 2024.02.06
[백준][Java] 14888. 연산자 끼워넣기  (0) 2024.02.05

문제

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안에 존재하는지 알아내면 된다. 모든 정수의 범위는 -2³¹보다 크거나 같고 2³¹보다 작다.

 

출력

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


코드

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

public class Main {

    static int N;
    static int[] arr;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        N = Integer.parseInt(br.readLine());

        arr = new int[N];
        StringTokenizer st = new StringTokenizer(br.readLine());

        for(int i = 0 ; i < N; i++){
            arr[i] = Integer.parseInt(st.nextToken());
        }
        Arrays.sort(arr);

        int M = Integer.parseInt(br.readLine());

        int[] targets = new int[M];
        st = new StringTokenizer(br.readLine());

        for(int i = 0; i < M; i++){
            targets[i] = Integer.parseInt(st.nextToken());
        }

        for(int i = 0; i < M; i++){
            int result = binarySearch(targets[i]);

            sb.append(result == -1 ? 0 : 1);
            sb.append("\n");
        }

        System.out.println(sb.toString());


    }

    static int binarySearch(int target){
        int left = 0;
        int right = N - 1;
        int mid;

        while(left <= right){

            mid = (left + right) / 2;
            
            if(arr[mid] == target){
                return mid; // 탐색 완
            }

            if(arr[mid] > target){
                right = mid - 1;
            }else {
                left = mid + 1;
            }

        }
    
        return -1; // 탐색 실패
       
    }
}

이 문제를 푸는 여러 방법이 있겠지만, 이분 탐색을 공부하는 김에 사용해보았다. left, right, mid는 A의 index를 의미하고, left와 right을 이용해 mid의 값을 갱신해나가며 값을 찾는다. A배열의 left부터 right까지 확인하여 target이 있는지 찾으면 된다. 이때, A 배열은 정렬이 되어 있어야 한다. left <= right 일 때까지 돌면서 반복하고, 만약 target 값과 A[mid]의 값이 같다면 target이 A 안에 있다는 의미이기 때문에 mid 값을 리턴한다. 만약 A[mid]가 target 보다 크다면, mid보다 왼쪽에 target이 있다는 의미이기 때문에 right을 `mid - 1`로 변경하여 A[left] ~ A[mid - 1] 까지의 배열만 탐색하도록 한다. 만약 A[mid] 가 target 보다 작다면 target이 mid 보다 오른쪽에 있다는 의미이기 때문에, 그 후에 탐색할 때는 A[mid + 1]부터 A[right]까지만 보면 된다. 따라서 left를 `mid + 1`로 변경하면 된다. 만약 while 문을 통과했는데 리턴하지 못했을 때는 A 배열에 target이 존재하지 않는 것이기 때문에 -1을 리턴한다.

728x90

문제

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

아래 <그림 1>과 같이 여러개의 정사각형칸들로 이루어진 정사각형 모양의 종이가 주어져 있고, 각 정사각형들은 하얀색으로 칠해져 있거나 파란색으로 칠해져 있다. 주어진 종이를 일정한 규칙에 따라 잘라서 다양한 크기를 가진 정사각형 모양의 하얀색 또는 파란색 색종이를 만들려고 한다.

전체 종이의 크기가 N×N(N=2k, k는 1 이상 7 이하의 자연수) 이라면 종이를 자르는 규칙은 다음과 같다.

전체 종이가 모두 같은 색으로 칠해져 있지 않으면 가로와 세로로 중간 부분을 잘라서 <그림 2>의 I, II, III, IV와 같이 똑같은 크기의 네 개의 N/2 × N/2색종이로 나눈다. 나누어진 종이 I, II, III, IV 각각에 대해서도 앞에서와 마찬가지로 모두 같은 색으로 칠해져 있지 않으면 같은 방법으로 똑같은 크기의 네 개의 색종이로 나눈다. 이와 같은 과정을 잘라진 종이가 모두 하얀색 또는 모두 파란색으로 칠해져 있거나, 하나의 정사각형 칸이 되어 더 이상 자를 수 없을 때까지 반복한다.

위와 같은 규칙에 따라 잘랐을 때 <그림 3>은 <그림 1>의 종이를 처음 나눈 후의 상태를, <그림 4>는 두 번째 나눈 후의 상태를, <그림 5>는 최종적으로 만들어진 다양한 크기의 9장의 하얀색 색종이와 7장의 파란색 색종이를 보여주고 있다.

입력으로 주어진 종이의 한 변의 길이 N과 각 정사각형칸의 색(하얀색 또는 파란색)이 주어질 때 잘라진 하얀색 색종이와 파란색 색종이의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 전체 종이의 한 변의 길이 N이 주어져 있다. N은 2, 4, 8, 16, 32, 64, 128 중 하나이다. 색종이의 각 가로줄의 정사각형칸들의 색이 윗줄부터 차례로 둘째 줄부터 마지막 줄까지 주어진다. 하얀색으로 칠해진 칸은 0, 파란색으로 칠해진 칸은 1로 주어지며, 각 숫자 사이에는 빈칸이 하나씩 있다.

 

출력

첫째 줄에는 잘라진 햐얀색 색종이의 개수를 출력하고, 둘째 줄에는 파란색 색종이의 개수를 출력한다.


코드

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

public class Main {
    static int N;
    static int[][] paper;
    static int white;
    static int blue;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        N = Integer.parseInt(br.readLine());
        paper = new int[N][N];

        StringTokenizer st = null;

        for(int i = 0; i < N; i++){
            st = new StringTokenizer(br.readLine());

            for(int j = 0; j < N; j++){
                paper[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        cut(0,0,N);

        System.out.println(white);
        System.out.println(blue);
    }

    /**
     *
     * @param x
     * @param y
     * @param size
     * 전부 같으려면 size*size 거나 0
     */
    static void cut(int x, int y, int size){
        int sum = 0;
        for(int i = 0; i < size; i++){
            for(int j = 0; j < size; j++){
                sum += paper[i+x][j+y];
            }
        }

        if(sum == 0){
            white++;
        }else if(sum == size*size){
            blue++;
        }else{
            cut(x, y, size/2);
            cut(x, y + size/2, size/2);
            cut(x + size/2, y, size/2);
            cut(x + size/2, y + size/2, size/2);
        }

    }
}

cut() 함수를 이용해 색종이를 잘랐다. 인자로는 현재 보고있는 색종이의 왼쪽 위의 x좌표와 y좌표, 그리고 size를 받았다. 먼저, 현재 색종이가 모두 같은 색으로 칠해져 있는지 확인해야 한다. 여러 방식이 있으나, 필자는 색종이의 적혀있는 수를 더했을 때 0이면 전부 흰색, size * size 면 전부 파란색이라고 판단하였다. 만약 0도 아니고, size*size도 아니라면, 다시 색종이를 cut() 해야 한다. 이때, 하나의 색종이의 가로와 세로의 중간 부분을 자르면 다음에 봐야하는 색종이는 4개가 되기 때문에 cut()을 4번 수행해야 한다. 

728x90

문제

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

N개의 수로 이루어진 수열 A1, A2, ..., AN이 주어진다. 또, 수와 수 사이에 끼워넣을 수 있는 N-1개의 연산자가 주어진다. 연산자는 덧셈(+), 뺄셈(-), 곱셈(×), 나눗셈(÷)으로만 이루어져 있다.

우리는 수와 수 사이에 연산자를 하나씩 넣어서, 수식을 하나 만들 수 있다. 이때, 주어진 수의 순서를 바꾸면 안 된다.

예를 들어, 6개의 수로 이루어진 수열이 1, 2, 3, 4, 5, 6이고, 주어진 연산자가 덧셈(+) 2개, 뺄셈(-) 1개, 곱셈(×) 1개, 나눗셈(÷) 1개인 경우에는 총 60가지의 식을 만들 수 있다. 예를 들어, 아래와 같은 식을 만들 수 있다.

  • 1+2+3-4×5÷6
  • 1÷2+3+4-5×6
  • 1+2÷3×4-5+6
  • 1÷2×3-4+5+6

식의 계산은 연산자 우선 순위를 무시하고 앞에서부터 진행해야 한다. 또, 나눗셈은 정수 나눗셈으로 몫만 취한다. 음수를 양수로 나눌 때는 C++14의 기준을 따른다. 즉, 양수로 바꾼 뒤 몫을 취하고, 그 몫을 음수로 바꾼 것과 같다. 이에 따라서, 위의 식 4개의 결과를 계산해보면 아래와 같다.

  • 1+2+3-4×5÷6 = 1
  • 1÷2+3+4-5×6 = 12
  • 1+2÷3×4-5+6 = 5
  • 1÷2×3-4+5+6 = 7

N개의 수와 N-1개의 연산자가 주어졌을 때, 만들 수 있는 식의 결과가 최대인 것과 최소인 것을 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수, 곱셈(×)의 개수, 나눗셈(÷)의 개수이다.

 

출력

첫째 줄에 만들 수 있는 식의 결과의 최댓값을, 둘째 줄에는 최솟값을 출력한다. 연산자를 어떻게 끼워넣어도 항상 -10억보다 크거나 같고, 10억보다 작거나 같은 결과가 나오는 입력만 주어진다. 또한, 앞에서부터 계산했을 때, 중간에 계산되는 식의 결과도 항상 -10억보다 크거나 같고, 10억보다 작거나 같다.


코드

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

// 0 -> + 기호
// 1 -> - 기호
// 2 -> x 기호
// 3 -> ÷ 기호
public class Main {
    static int[] numbers;
    static int[] picked;
    static LinkedList<Integer> operators; // 연산자 모음
    static boolean[] visited; // 연산자 방문 여부 저장
    static int N; 

    static int min = Integer.MAX_VALUE;
    static int max = Integer.MIN_VALUE;

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

        N = Integer.parseInt(br.readLine());

        numbers = new int[N];
        visited = new boolean[N-1];
        picked = new int[N-1];
        operators = new LinkedList<>();

        StringTokenizer st = new StringTokenizer(br.readLine());

        for(int i = 0; i < N; i++){
            numbers[i] = Integer.parseInt(st.nextToken());
        }

        st = new StringTokenizer(br.readLine());
        for(int i = 0; i < 4; i++){
            int cnt = Integer.parseInt(st.nextToken());

            for(int j = 0; j < cnt; j++){
                operators.push(i);
            }
        }

        permutation(0);

        System.out.println(max);
        System.out.println(min);
    }

    /**
     * 
     * @param cnt : 현재까지 선택한 요소의 개수
     */
    static void permutation(int cnt){
        if(cnt == N-1){
            getMinMaxValue();
            return;
        }

        for(int i = 0; i < N-1; i++){
            if(visited[i]) continue;

            visited[i] = true;
            picked[cnt] = operators.get(i);

            permutation(cnt+1);

            visited[i] = false;
        }

    }

    static void getMinMaxValue(){
        int value = numbers[0];

        for(int i = 0; i < N-1; i++){
            value = calculate(value, numbers[i+1], picked[i]);
        }

        if(value < min) min = value;
        if(value > max) max = value;
    }

    static int calculate(int num1, int num2, int operator){
        int result = 0;

        switch (operator){
            case 0:
                result = num1 + num2;
                break;
            case 1:
                result = num1 - num2;
                break;
            case 2:
                result = num1 * num2;
                break;
            case 3:
                result = num1 / num2;
                break;
        }

        return result;
    }

}

순열로 문제를 풀었다. 각 연산자를 0, 1, 2, 3으로 맵핑하였고, caculate 함수에서 num1과 num2를 계산해주었다. 또한 getMinMaxValue() 함수에서 value 값을 누적해가며 최종 값을 계산하였고, min과 max 값을 갱신하였다.

728x90

문제

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

숫자 카드는 정수 하나가 적혀져 있는 카드이다. 상근이는 숫자 카드 N개를 가지고 있다. 정수 M개가 주어졌을 때, 이 수가 적혀있는 숫자 카드를 상근이가 몇 개 가지고 있는지 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 상근이가 가지고 있는 숫자 카드의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 숫자 카드에 적혀있는 정수가 주어진다. 숫자 카드에 적혀있는 수는 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다.

셋째 줄에는 M(1 ≤ M ≤ 500,000)이 주어진다. 넷째 줄에는 상근이가 몇 개 가지고 있는 숫자 카드인지 구해야 할 M개의 정수가 주어지며, 이 수는 공백으로 구분되어져 있다. 이 수도 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다.

 

출력

첫째 줄에 입력으로 주어진 M개의 수에 대해서, 각 수가 적힌 숫자 카드를 상근이가 몇 개 가지고 있는지를 공백으로 구분해 출력한다.


코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

public class baek_10816_숫자카드2 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        Map<Integer, Integer> numbers = new HashMap<>();

        int N = Integer.parseInt(br.readLine());
        String[] strs = br.readLine().split(" ");

        for(int i = 0; i < strs.length; i++){
            int number = Integer.parseInt(strs[i]);
            numbers.put(number, numbers.getOrDefault(number, 0) + 1);
        }

        int M = Integer.parseInt(br.readLine());
        String[] targets = br.readLine().split(" ");

        for(int i = 0; i < targets.length; i++){
            int target = Integer.parseInt(targets[i]);
            sb.append(numbers.getOrDefault(target, 0)).append(" ");
        }
        System.out.print(sb.toString());
    }
}

 

시간 제한이 1초인 것을 보고 절대 이중포문으로 풀면 안된다고 생각했다. 자료구조 중 Map이 떠올랐고 key에는 숫자 카드의 정수를 저장했고, value에는 해당 key가 등장한 횟수를 저장했다. 상근이가 갖고 있는 개수를 알고 싶은 숫자카드가 상근이가 갖고 있지 않을 수도 있기 때문에 map에서 가져올 때 getOrDefault를 이용해서 처리를 해주었다.

처음에는 StringBuilder를 사용하지 않았는데, 시간초과가 나서 사용하는 것으로 변경하였다.

728x90

문제

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

세준이는 양수와 +, -, 그리고 괄호를 가지고 식을 만들었다. 그리고 나서 세준이는 괄호를 모두 지웠다.

그리고 나서 세준이는 괄호를 적절히 쳐서 이 식의 값을 최소로 만들려고 한다.

괄호를 적절히 쳐서 이 식의 값을 최소로 만드는 프로그램을 작성하시오.

 

입력

첫째 줄에 식이 주어진다. 식은 ‘0’~‘9’, ‘+’, 그리고 ‘-’만으로 이루어져 있고, 가장 처음과 마지막 문자는 숫자이다. 그리고 연속해서 두 개 이상의 연산자가 나타나지 않고, 5자리보다 많이 연속되는 숫자는 없다. 수는 0으로 시작할 수 있다. 입력으로 주어지는 식의 길이는 50보다 작거나 같다.

 

출력

첫째 줄에 정답을 출력한다.


코드

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


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

        String[] minusArr = br.readLine().split("-"); // - 기준으로 나누기

        // 최종 결과
        int result = 0;

        for(int i = 0; i < minusArr.length; i++){
            String[] plusArr = minusArr[i].split("\\+"); // + 기준으로 나누기

            int sum = 0; // + 부호를 갖고있는 number 저장하는 변수

            for(int j = 0; j < plusArr.length; j++){
                sum += Integer.parseInt(plusArr[j]);
            }

            if(i == 0){ // - 로 나누어진 배열의 가장 앞 인덱스에 들어있는 원소는 더해주어야 함
                result += sum;
            }else{
                result -= sum;
            }
        }

        System.out.println(result);
    }
}

 

값을 최소로 만들기 위해서는 열린 괄호가 `-` 바로 뒤, 닫힌 괄호가 그 다음에 등장하는 `-` 의 바로 앞에 있으면 된다. 즉 `-` 연산자를 기준으로 연산이 이루어지면 되기 때문에, `-`를 기준으로 split를 하여 minusArr에 저장하였다. 즉 minusArr의 각 원소 계산을 마치고 `-` 연산을 해주면 된다. minusArr의 원소는 `50+25+45` 이런식으로 들어가 있을 것이기 때문에 minusArr의 원소도 `+`를 기준으로 split을 해서 plusArr에 저장하고, plusArr에 들어있는 원소들을 다 더했다. 그리고 더한 결과 값을 최종 result 에서 빼주었다. 이때 minusArr[0]은 빼주면 안되고 더해주어야 한다. minusArr이 `-`를 기준으로 split 하였지만 처음 index는 앞에 `-` 부호가 없는 상태이기 때문이다.

728x90

문제

Backward numbers are numbers written in ordinary Arabic numerals but the order of the digits is reversed. The first digit becomes the last, and vice versa. For example, the number 1245 becomes 5421. Note that all leading zeroes are omitted. This means that if the number ends with a zero, the zero is lost by the reversal; e.g., 1200 gives 21. Also note that the reversed numbers never have any trailing zeroes.

We need to calculate with backward numbers and your task is to write a program that can add two backward numbers and output their sum as a backward number. Of course, the result is not unique (e.g., 21 could be 12, 120, or 1200 before the reversal). Thus, we assume that no zeroes were lost in the reversal (e.g., we assume that the original number was 12).

 

입력

The input consists of N cases. The first input line contains only the integer N, and we assume that N > 0. The follow the cases. Each case consists of exactly one line with two non-negative integers separated by a space. These are the reversed numbers you are to add.

We assume that all numbers are in the range 0 ≤ n < 109.

 

출력

For each case, print exactly one line containing only one integer—the backward sum of the two backward numbers. Omit any leading zeroes in the output.


코드

import java.io.*;

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

        int N = Integer.parseInt(br.readLine());

        for(int i = 0; i < N; i++){
            String[] arr = br.readLine().split(" ");
            for(int j = 0; j <2; j++){
                sb = new StringBuffer(arr[j]);
                arr[j] = sb.reverse().toString();
            }
            int sum = Integer.parseInt(arr[0]) + Integer.parseInt(arr[1]);
            String sumSt = Integer.toString(sum);
            sb = new StringBuffer(sumSt);
            System.out.println(Integer.parseInt(sb.reverse().toString()));
        }

    }
}

 

풀이

이 문제는 두 수를 입력받고 각각 reverse 한 뒤 더해서 다시 reverse 한 값을 출력하는 것이다.

예를 들어 예제 입력 1에 있는 24와 1의 경우, 24를 거꾸로 바꿔서 42로, 1을 거꾸로 바꿔서 1로 해놓고 더하면 43이 된다. 그리고 다시 거꾸로 바꾸면 34가 된다. 즉 답은 34가 된다.

또 다른 예시인 794의 경우, 305를 바꾼 503과 794를 바꾼 497을 더해준다. 그럼 1000이 나오고, 1000을 거꾸로 바꾸면 0001이 된다. 이때 숫자의 경우 앞에 있는 0은 생략해주기 때문에 1이 나오게 된다.

 

숫자를 int로 입력받아서 거꾸로 돌리는 게 복잡할 것 같아 일단 입력을 다 String으로 받고, StringBuffer에 있는 함수인 reverse를 이용해 입력받은 숫자를 거꾸로 돌려주었다. 거꾸로 돌린 숫자들을 Integer.parseInt()로 숫자로 바꾼 후 더해주었고, 그걸 다시 String으로 변환해 reverse함수로 거꾸로 돌려주었다.

앞에 0이 있는 경우를 대비해 출력할 때 int로 형을 변환해서 출력해주었다.

728x90

'Algorithm > 백준' 카테고리의 다른 글

[백준][Java] 10816. 숫자 카드 2  (0) 2024.02.05
[백준][Java] 1541. 잃어버린 괄호  (0) 2024.01.31
[백준][Python] 7360. Undercut  (0) 2022.04.30
[백준][Java] 1912. 연속합  (0) 2022.04.28
[백준][Python] 19604. Art  (0) 2022.04.25

문제

Undercut is a card game where two players each have five cards numbered one through five. At each round, each player selects a card, then simultaneously reveals it. If the cards are of equal value, there is no score. Otherwise, there are two cases: the two cards are exactly one point apart (this is called an undercut), or the cards are more than one point apart. In the latter case, the person revealing the larger of the cards gets the number of points on the larger card. In the case of an undercut the player with the lower card gets the sum of the two cards. The exception to this is when the cards are 1 and 2, in which case the player with the lower card gets 6 points (instead of only 3 points). After each round, the cards are returned to the hands and they play another round.

For example, if there are 5 rounds and player A plays (in this order) 5, 3, 1, 3, 5 and player B plays 3, 3, 3, 3, 4, then the scoring for each round would be: A gets 5 points, no points, B gets 3 points, no points, B gets 9 points. The totals would be A: 5, B: 12.

In this problem you will be given card plays for both players and must determine the final scores.

 

입력

There will be multiple input instances. Each instance will be one game. The first line of input for a game will be an integer n <= 20. (A value of n = 0 terminates input.) The next two lines will each contain n integers between 1 and 5 inclusive indicating the cards played on each of n rounds. The first line are player A's card plays and the second line are player B's card plays.

 

출력

Each input instance should generate one line of output of the form:

A has a points. B has b points.

where the value of a and b are for you to determine. A blank line should separate output lines.


코드

import sys

if __name__ == '__main__':

    while True:
        plays = int(sys.stdin.readline().rstrip())
        
        if plays==0:
            break

        A_point = 0
        B_point = 0

        A = list(map(int, sys.stdin.readline().rstrip().split(" ")))
        B = list(map(int, sys.stdin.readline().rstrip().split(" ")))

        for i in range(plays):
            # 한 개 차이면 적은 수를 가진 애가 이김
            # 자신의 카드 넘버와 상대의 카드 넘버 만큼
            if abs(A[i]-B[i]) == 1:
                if A[i] > B[i]:
                    if A[i] == 2:
                        B_point += 6
                    else:
                        B_point += A[i] + B[i]
                else:   # B[i] > A[i]
                    if B[i] == 2:
                        A_point += 6
                    else:
                        A_point += A[i] + B[i]
            elif A[i] < B[i]:
                B_point += B[i]
            elif A[i] > B[i]:
                A_point += A[i]

        print("A has {} points. B has {} points.\n".format(A_point, B_point))

 

풀이

문제를 먼저 해석해보자면, A와 B가 카드게임을 하는데 이때 이기는 조건이 여러 가지이다.

① 두 카드의 숫자 차이가 1보다 클 경우, 숫자가 큰 카드를 가진 사람이 이긴다. 이때, 얻는 점수는 자신의 카드 숫자만큼이다.(큰 카드)

② 두 카드의 숫자 차이가 1일 경우, 숫자가 작은 카드를 가진 사람이 이긴다. 이때, 얻는 점수는 자신의 카드와 상대방의 카드 숫자를 더한 값이다.

③ ②번의 예외로, 두 카드가 1과 2일 경우, 무조건 낮은 카드를 가진 사람이 6점을 얻는다.

④ 두 카드의 숫자가 같을 경우, 무승부로 아무도 점수를 얻지 않는다.

 

위 조건에 맞춰 풀면 된다. 입력을 0이 나올 때까지 받기 때문에 무한 반복을 하고 0이 나오면 break 할 수 있도록 코드를 작성했다. 첫 번째 if 문에서는 A와 B의 카드 차가 1인지 확인했다. 이때 어느 수가 더 큰지 모르기 때문에 abs() 함수를 이용해 절댓값을 씌워서 1인지 확인했다. 그 후, 두 개의 카드가 각각 1과 2라면 6점을 더해주었다. 이때 이미 if 문에서 카드의 차이가 1인지를 확인했기 때문에 A가 B보다 큰 경우, A가 2인지만 확인해주면 B는 자동으로 1이 되므로 A만 조건식에 써주었다. 두 개의 카드가 1과 2가 아니라면 조건 ②에 따라서 두 카드의 합을 점수에 더해주었다.

두 번째와 세 번째 if문의 경우는 조건 ①에 따른 식으로 큰 카드의 수를 점수에 더해주었다.

728x90

'Algorithm > 백준' 카테고리의 다른 글

[백준][Java] 1541. 잃어버린 괄호  (0) 2024.01.31
[백준][Java] 6721. Backward numbers  (0) 2022.04.30
[백준][Java] 1912. 연속합  (0) 2022.04.28
[백준][Python] 19604. Art  (0) 2022.04.25
[백준][Java] 17548. Greetings!  (0) 2022.04.25

+ Recent posts