문제 링크 : https://www.acmicpc.net/problem/15552


단순한 A+B 지만 빠른 입출력을 사용해야 합니다.


자바 입력에 대한 포스팅 https://bcp0109.tistory.com/37


자바 출력에 대한 포스팅 https://bcp0109.tistory.com/38



입력때 Scanner


출력때 System.out.print 


둘중 하나만 사용하더라도 시간 초과가 납니다.


입력엔 BufferedReader를 사용하고


출력엔 BufferedWriter나 StringBuilder를 사용하면 됩니다.


둘의 차이는 거의 나지 않으므로 편한걸 사용하시면 됩니다.


저는 개인적으로 BufferedReader와 비슷하게 생긴 BuffedWriter를 사용하는 편입니다.


연습 문제 : https://bcp0109.tistory.com/39

 

입력과 마찬가지로 출력에도 여러가지 방법이 있습니다.

 

가장 간단한 방법으로는 System.out.print가 있지만 굉장히 느립니다.

 

보통 알고리즘 답은 많은 출력을 필요로 하지 않지만 여러 개의 테스트케이스에 대해서 출력을 할 때는 시간 초과가 발생할 수 있습니다.

 

따라서 많은 출력이 필요할 땐 BufferedWriter나 StringBuilder를 사용하는 것을 권장합니다.

 

이 둘의 특징은 데이터를 모아두었다가 한번에 출력합니다.

 

BufferedWriter의 경우네는 flush()를 사용하고

 

StringBuilder의 경우에는 자체적으로 String들을 이어붙인 것이기 때문에 StringBuilder를 출력하면 됩니다.

 

BufferedWriter를 사용하는 경우에는 입력할 때와 마찬가지로 throws Exception 을 붙여주거나 try 내에서 사용하시면 됩니다.

 

** 간단한 비교를 위해 10만개의 데이터를 출력해보았습니다.

 

소요시간은 StringBuilder가 가장 빨랐지만 사실 큰 차이는 없으므로 편한 것을 사용하시면 됩니다.

 

출력하는 데이터가 적다면 System.out을 사용하여도 괜찮습니다.

 

'알고리즘 문제 > 공부' 카테고리의 다른 글

2차원 배열 회전 Rotate (Java)  (1) 2020.03.21
자바 Collections 시간복잡도 (Big-O)  (0) 2019.04.02
자바 입력 (Java)  (0) 2019.01.05
위상정렬 Topological Sort (Java)  (1) 2018.12.28
부분집합 PowerSet (Java)  (2) 2018.12.27

연습 문제 : https://bcp0109.tistory.com/39

 

자바 입력 방법은 Scanner과 BufferedReader가 있습니다.

 

Scanner가 쉽고 간단하여 저도 처음 문제를 풀 때는 사용하였지만, 사실 알고리즘 문제풀이에는 적절하지 않습니다.

 

많은 입력을 받아야 하는 경우에는 시간초과가 날 수 있기 때문입니다.

 

따라서 BufferedReader를 사용하는 것에 익숙해지는 것을 추천합니다.

 

** BufferedReader를 사용하기 위해서는 try문 내에서 사용하거나 메소드 끝에 throws Exception 구문을 붙여주셔야 합니다!

 

한 줄에 여러 개의 입력을 받는 경우에는 StringTokenizer 를 사용하면 됩니다.

 

'알고리즘 문제 > 공부' 카테고리의 다른 글

자바 Collections 시간복잡도 (Big-O)  (0) 2019.04.02
자바 출력 (Java)  (0) 2019.01.05
위상정렬 Topological Sort (Java)  (1) 2018.12.28
부분집합 PowerSet (Java)  (2) 2018.12.27
조합 Combination (Java)  (7) 2018.12.27

문제 링크 : https://www.acmicpc.net/problem/2438


굉장히 간단한 별찍기 문제지만 자바의 입/출력에 따라서 성능 차이가 납니다.


System.out.print는 속도가 느리기 때문에 많은 출력이 필요할 때는 BufferedWriter나 StringBuilder를 사용하는 것이 좋습니다.


N 만큼의 줄을 먼저 for문으로 돌면서 현재 i 만큼만 별을 출력하는 2중 for문을 사용하여 해결할 수 있습니다.


문제 링크 : https://www.acmicpc.net/problem/1924


월, 일을 입력받아서 요일을 출력하는 문제입니다.


각 월의 일수를 담아놓는 month 배열을 사용하여서


현재 월 이전까지의 모든 일수를 더합니다.


예를 들어 3월이면 1월~2월까지의 일수를 더합니다.


그 후에 현재 일수만큼 더한 다음 7을 나눈 나머지를 요일로 출력해주시면 됩니다.


문제 링크 : https://www.acmicpc.net/problem/1157


입력 받은 String 에서 가장 많이 사용되는 알파벳을 출력하는 문제입니다.


만약 가장 많이 사용된 알파벳의 수가 동일하다면 ? 을 출력합니다.


알파벳 갯수만큼 사이즈 26인 배열 arr 을 선언하여 알파벳의 갯수를 담아둡니다.


max값보다 크다면 새로운 알파벳을 answer 값에 갱신해주고 만약 max 값과 똑같다면 '?' 값을 answer에 입력합니다.


idx 값은 소문자에서 -'a' 를 해주면 인덱스만큼 구할 수 있습니다.


이후에 값을 넣을 때는 대문자로 출력해야 하기 때문에 i+65 값을 바꿔주면 됩니다.


Problem


톱니바퀴의 초기 상태와 톱니바퀴를 회전시킨 방법이 주어졌을 때, 최종 톱니바퀴의 상태를 구하여


접수의 합을 출력하는 문제입니다.



Solution

특별한 알고리즘은 없이 톱니의 상태에 맞추어 톱니바퀴를 회전시키는 단순 구현 시뮬레이션 문제입니다.


이 문제에서 주의해야 할 점은 두 가지 입니다.


  1. 좌, 우 톱니바퀴의 회전 여부는 회전이 이루어지기 전에 결정된다.
  2. 톱니바퀴의 번호는 0 ~ 3 이 아니라 1 ~ 4 다.


1 번은 말그대로 톱니바퀴를 회전시키기 전에 모든 톱니바퀴에 대해서 회전 여부가 결정되어야 합니다.


저는 재귀를 통해 현재 상태에서 N 극과 S 극을 비교하여 회전 여부를 결정하고


가장 끝에있는 1 번 또는 4 번 부터 회전시켰습니다.


2 번은 사소한 실수인데 보통 톱니바퀴를 배열로 선언하다보니 0 ~ 3 이라고 착각하는 경우가 있습니다.


구현하는 함수는 톱니바퀴를 회전시키는 rotate 함수와


좌, 우 톱니바퀴 회전 여부를 판단하기 위한 leftright 함수입니다.


톱니바퀴를 회전시키는 건 for 문을 사용하면 됩니다.


leftright 함수는 재귀를 통하여 모든 톱니바퀴의 회전 여부를 검사합니다.


톱니바퀴의 번호가 0 ~ 3 이라고 생각하고 2 번을 회전시키라는 명령이 들어오면 다음과 같은 로직으로 진행됩니다.


example


회전 여부를 확인하기 위해 호출되는 것이 leftright 함수고 시간 순서대로 화살표를 따라서 진행됩니다.


코드를 보시면 쉽게 이해하실 수 있습니다.


파이썬은 deque 를 자료구조를 사용하였고 deque 에 있는 rotate 함수를 사용하였습니다.



Java Code

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

class Main {
    static int stoi(String s) {
        return Integer.parseInt(s);
    }

    // 톱니바퀴 [번호][방향]
    static int[][] arr = new int[4][8];

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

        for (int i = 0; i < 4; i++) {
            String s = br.readLine();
            for (int j = 0; j < 8; j++) {
                arr[i][j] = s.charAt(j) - '0';
            }
        }

        int k = stoi(br.readLine());

        // 톱니바퀴 회전
        for (int i = 0; i < k; i++) {
            st = new StringTokenizer(br.readLine());
            int idx = stoi(st.nextToken());
            int dir = stoi(st.nextToken());

            // 톱니바퀴 번호는 1~4, 인덱스는 0~3
            solution(idx - 1, dir);
        }

        // 점수 계산
        int score = 0;
        for (int i = 0; i < 4; i++) {
            score += arr[i][0] * (1 << i);
        }

        System.out.println(score);
    }

    // 9시 방향은 2, 3시 방향은 6
    static void solution(int idx, int dir) {
        left(idx - 1, -dir);
        right(idx + 1, -dir);
        rotate(idx, dir);
    }

    // 왼쪽에 있던 톱니바퀴 회전 여부 결정
    static void left(int idx, int dir) {
        if (idx < 0) return;

        if (arr[idx][2] != arr[idx + 1][6]) {
            left(idx - 1, -dir);
            rotate(idx, dir);
        }
    }

    // 오른쪽에 있던 톱니바퀴 회전 여부 결정
    static void right(int idx, int dir) {
        if (idx > 3) return;

        if (arr[idx][6] != arr[idx - 1][2]) {
            right(idx + 1, -dir);
            rotate(idx, dir);
        }
    }

    // dir = 1 시계방향, dir = -1 반시계방향
    static void rotate(int idx, int dir) {
        if (dir == 1) {
            int temp = arr[idx][7];

            for (int i = 7; i > 0; i--) {
                arr[idx][i] = arr[idx][i - 1];
            }

            arr[idx][0] = temp;

        } else {
            int temp = arr[idx][0];

            for (int i = 0; i < 7; i++) {
                arr[idx][i] = arr[idx][i + 1];
            }

            arr[idx][7] = temp;
        }
    }
}



Python Code

# -*- coding: utf-8 -*-
 
import sys
import collections
 
wheels = []
turns = []
 
def turnLeft(i, d):
    if i < 0:
        return
 
    if wheels[i][2] != wheels[i+1][6]:
        turnLeft(i-1, -d)
        wheels[i].rotate(d)
 
def turnRight(i, d):
    if i > 3:
        return
 
    if wheels[i][6] != wheels[i-1][2]:
        turnRight(i+1, -d)
        wheels[i].rotate(d)
 
def solve():
    for turn in turns:
        [idx, direction] = turn
        
        turnLeft(idx-1, -direction)
        turnRight(idx+1, -direction)
 
        wheels[idx].rotate(direction)
 
if __name__ == '__main__':
    for i in range(4):
        wheels.append(collections.deque(list(sys.stdin.readline())[:8]))
 
    K = int(sys.stdin.readline())
 
    for i in range(K):
        v1, v2 = map(int, sys.stdin.readline().split())
        turns.append([v1-1, v2])
 
    solve()
    sumVal = 0
    for i, wheel in enumerate(wheels):
        sumVal += int(wheel[0]) * (1 << i)
    print(sumVal)


문제 링크 : https://www.acmicpc.net/problem/9466


싸이클을 형성하지 못하는 노드 갯수를 찾는 문제입니다.


일반적인 DFS로 모든 노드를 탐색하면 시간 초과가 납니다.


테스트케이스가


2 3 4 5 1


이렇게 주어졌을 때 모든 DFS를 탐색한다면


1->2->3->4->5

2->3->4->5->1

3->4->5->1->2

4->5->1->2->3

5->1->2->3->4


O(n^2)의 시간복잡도를 갖게 되고, 최대 10만의 n 값으로 탐색한다면 시간 초과가 날 수밖에 없습니다.


따라서 이 문제의 핵심은 더이상 방문할 필요가 없는 노드를 구분해서 쓸데없는 탐색을 막는 것입니다.


그렇다면 어떻게 구분 할 것인가?


일단 이 문제는 모든 노드들은 무조건 다른 노드 한개를 가리킨다라는 조건이 있습니다.


이 말은 DFS 탐색이 끝나기 위해선 무조건 싸이클에 들어가야 한다는 뜻입니다.


예제 테스트케이스를 봐도


1->3->3

2->1->3->3

3->3


어디서 시작해도 끝은 항상 싸이클입니다.


이 점을 이용한다면 1, 2, 3 번 노드를 모두 탐색 할 필요 없이 1번 노드 탐색만으로도 싸이클을 만난다는 사실을 알 수 있습니다.


그렇다면 2번과 3번 노드는 끝까지 탐색할 필요가 없게 됩니다.


2번 노드를 탐색해서 1번 노드를 찾아 들어간다면 1번에서 탐색한 결과와 똑같이 나오기 때문에 이를 방지하여 중복을 최소화 합니다.


그래서 기본적으로 DFS 탐색에 사용되는 visited 배열 외에 finished 배열을 하나 더 만들어줍니다.


visited 배열은 단순히 방문 여부를 체크하는 것이고, finished 배열은 방문한 노드에서 싸이클을 이미 뽑아냈었는가를 확인합니다.


1->3->3


1번 노드 탐색을 할 때는 visited[3] 값이 true기 때문에 탐색이 끝납니다.


탐색의 끝은 항상 싸이클이기 때문에 3번 노드는 무조건 싸이클을 형성하는 노드 중 하나라는 사실을 알 수 있습니다.


그럼 3번 노드부터 연결된 노드를 탐색하는 for문을 통해 3번 노드와 싸이클을 이루는 노드 갯수를 카운트합니다.


이 작업이 끝나면 1번과 3번 노드는 더이상 사용할 필요가 없기 때문에 finished 값을 true로 바꿔줍니다.


2->1->3->3


2번 노드 탐색을 할 때는 1번 노드를 만납니다.


하지만 1번 노드는 이미 사용한 노드라 finished 값이 true입니다.


1번 노드와 같은 싸이클을 만날 것이기 때문에 2번 노드는 더이상 탐색을 진행하지 않고 빠져나옵니다.


같은 방식으로 모든 노드를 탐색하면 O(n)의 시간복잡도로 탐색을 끝낼 수 있습니다.


+ Recent posts