문제 풀이 날짜: 2023.07.26
포스트 작성일: 2023.07.26

 

* 학습 목적으로 작성하는 글입니다. 풀이가 정석적이지 못할 수도 있습니다.

 

문제 출처

SWEA 1220번: Magnetic (D3)

 

키워드

배열 다루기, 문자열, 구현

 

풀이 접근법

  • 규칙을 찾는 구현 문제. 자석의 움직임을 구현하지 않아도 된다.
  • 자석 각각의 개수가 아닌, 교착상태가 된 덩어리의 수를 세야 하므로 주의한다.
  • 배열의 세로줄을 탐색하면서, 0이 아닌 것을 모두 String에 이어붙어서 규칙을 체크한다.
    • 북쪽이 N극이고 남쪽이 S극이므로 각각의 자석이 모두 끌려간 후, 테이블 위에는 1-2 순서로 붙은 자석만 남아있게 된다. (1-1-2, 1-2-2 모양 또한 가능)
    • 이때 String이 1로 시작하고 2로 끝나는 것을 찾을 게 아니라, “12”라는 substring을 몇 개 포함하는지를 알아내면 남아있는 자석의 수를 알 수 있다.
    • "12" 모양을 제외하고 그 위와 아래에 붙어있는 자석은 어차피 한 덩어리로 뭉쳐지므로 고려하지 않아도 된다.

 

코드

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

class Solution
{
	private static final int SIZE = 100;
	
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int T = 10;
		
		int[][] matrix = new int[SIZE][SIZE];
		
		StringTokenizer st;
		StringBuilder sb = new StringBuilder();
		for(int test_case = 1; test_case <= T; test_case++) {
			int N = Integer.parseInt(br.readLine());
            
			for(int i = 0; i < SIZE; i++) {
				st = new StringTokenizer(br.readLine());
				for(int j = 0; j < SIZE; j++) {
					matrix[i][j] = Integer.parseInt(st.nextToken());
				}
			}
			
			int answer = 0;
			StringBuilder temp;
			for(int j = 0; j < SIZE; j++) {
				temp = new StringBuilder();
				for(int i = 0; i < SIZE; i++) {
					if(matrix[i][j] != 0) {
						temp.append(matrix[i][j]);
					}
				}
				
				String result = temp.toString();
				result = result.replaceAll("12", "*");
				for(int i = 0; i < result.length(); i++) {
					if(result.charAt(i) == '*') {
						answer++;
					}
				}
			}
			
			sb.append("#").append(test_case).append(" ")
			.append(answer).append("\n");
		}
		
		System.out.println(sb);
	}
}

 

git 링크

(git 링크 첨부)

문제 풀이 날짜: 2023.07.26
포스트 작성일: 2023.07.26

 

* 학습 목적으로 작성하는 글입니다. 풀이가 정석적이지 못할 수도 있습니다.

 

문제 출처

Software Expert Academy 2007번: 패턴 마디의 길이 (D2)

 

키워드

문자열

 

풀이 접근법

  • 두 구간의 substring을 떠서 내용을 비교한다. 각각 [0 ~ i) 와 [i ~ 2i)까지 따오면 된다.
    • 마디의 길이가 최대 10밖에 되지 않으므로 이러한 메소드를 사용해도 시간초과가 나지 않는다.
    • input의 길이를 잘 점검해서 가능한 간단한 풀이를 선택하자. 만약 input의 크기가 크다면 다른 풀이를 구상해야 하지만, 작다면 가능한 API들을 활용하자.

 

코드

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

class Solution {
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int T = Integer.parseInt(br.readLine());
		
		StringBuilder sb = new StringBuilder();
		for(int test_case = 1; test_case <= T; test_case++) {
			String str = br.readLine();
			
			for(int i = 1; i < str.length() / 2; i++) {
				String s1 = str.substring(0, i);
				String s2 = str.substring(i, 2 * i);
				
				if(s1.equals(s2)) {
					sb.append("#").append(test_case).append(" ")
						.append(s1.length()).append("\n");
					break;
				}
			}
		}

		System.out.println(sb);
	}
}

 

input의 크기를 고려하지 않고 접근해서 어려운 방식으로 풀었던 문제이다. 답이 나오지 않아 다른 블로그 글을 참고하여 풀이했다.

 

참고한 블로그

https://haerang94.tistory.com/194

 

git 링크

(git 링크 첨부)

문제 풀이 날짜: 2023.07.26
포스트 작성일: 2023.07.26

 

* 학습 목적으로 작성하는 글입니다. 풀이가 정석적이지 못할 수도 있습니다.

 

문제 출처

백준 온라인 저지 2941번: 크로아티아 알파벳 (실버5)

 

키워드

문자열

 

풀이 접근법

  • replace() 메소드를 중점적으로 활용한다.
  • 여기서 replace() 할 대상을 String[] 배열에 담아 참조하는 것이 포인트이다. 이 문제에서는 크로아티아 알파벳 몇 가지가 제시되었으므로, 그것을 배열에 담아 참조하면서 문자열이 크로아티아 알파벳을 포함하는지를 검사한다.
  • replace()는 문자열 단위로도 교체할 수 있으므로 크로아티아 알파벳을 어떤 문자 한 개(예: *, ! 등등)로 치환하고 전체 길이를 잰다.

 

코드

import java.io.*;

public class Main {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		String input = br.readLine();
		
		String[] keys = {"c=", "c-", "dz=", "d-", "lj", "nj", "s=", "z="};
		
		for(int i = 0; i < keys.length; i++) {
			if(input.contains(keys[i])) {
				input = input.replace(keys[i], "*");
			}
		}
		System.out.println(input.length());
	}

}

문자열을 replace하여 원하는 결과를 얻는 방식은 여러문제에 응용되기 좋으니 풀이법을 잘 기억해두자.

 

git 링크

(git 링크 첨부)

문제 풀이 날짜: 2023.07.25
포스트 작성일: 2023.07.25

 

* 학습 목적으로 작성하는 글입니다. 풀이가 정석적이지 못할 수도 있습니다.

 

문제 출처

Software Expert Academy 1493번: 수의 새로운 연산 (D3)

 

키워드

배열 다루기, 구현

 

풀이 접근법

  • 수의 증가 규칙을 찾아 처음부터 배열을 초기화 하였다. 육안으로 보기에는 파란색 화살표를 따라 대각선 아래 방향으로 증가 규칙이 있는 듯하나, 가로줄과 세로줄의 증가 규칙을 따로 찾는 것이 구현하기 쉬웠다.
  • 세로줄은 아래에서 위로 1, 2, 3, 4, 5, ... 씩 증가한다.
  • 첫 번째 가로줄은 왼쪽에서 오른쪽으로 2, 3, 4, ... 씩 증가
    • 두 번째 가로줄은 왼쪽에서 오른쪽으로 3, 4, 5, ... 씩 증가
    • 세 번째 가로줄은 왼쪽에서 오른쪽으로 4, 5, 6, ... 씩 증가
    • → 가로줄 윗쪽으로 갈 수록(i값이 커질 수록) 시작점이 1씩 늘어난다.
  • 이 규칙대로 배열을 초기화 하면서 Map<Integer, Point><배열값, 위치> 쌍을 저장한다.
    • 배열이 커질 수록 반복문으로 원하는 값을 찾는데 오래 걸린다.
    • Map을 이용하면 입력으로 온 p, q의 위치값을 빠르게 찾을 수 있다. 

 

코드

import java.util.*;
import java.io.*;
import java.awt.Point;

class Solution
{
	private static final int MAX_SIZE = 300;
	
	private static Map<Integer, Point> map = new HashMap<>();
	private static int[][] matrix = new int[MAX_SIZE + 1][MAX_SIZE + 1]; //0은 사용 안 함
    
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int T = Integer.parseInt(br.readLine());

		//1.규칙에 맞게 matrix를 생성
		initializeMatrix();
		
		StringBuilder sb = new StringBuilder();
		for(int test_case = 1; test_case <= T; test_case++) {
			String[] temp = br.readLine().split(" ");
			
			int p = Integer.parseInt(temp[0]);
			int q = Integer.parseInt(temp[1]);
			
			//2.주어진 연산 수행
			Point p1 = map.get(p);
			Point p2 = map.get(q);
			
			int nx = p1.x + p2.x;
			int ny = p1.y + p2.y;
			
			int answer = matrix[ny][nx];
			
			sb.append("#").append(test_case).append(" ").append(answer).append("\n");
		}
		System.out.println(sb);
	}

	private static void initializeMatrix() {
		//세로줄 초기화
		int k = 1;
		matrix[1][1] = 1;
		map.put(matrix[1][1], new Point(1, 1));
		
		for(int i = 2; i <= MAX_SIZE; i++) {
			matrix[i][1] = matrix[i - 1][1] + (k++);
			map.put(matrix[i][1], new Point(1, i)); //row & col 순서 주의
		}
		
		//가로줄 초기화
		k = 2;
		for(int i = 1; i <= MAX_SIZE; i++) {
			for(int j = 2; j <= MAX_SIZE; j++) {
				matrix[i][j] = matrix[i][j - 1] + (k + j - 2);
				map.put(matrix[i][j], new Point(j, i)); //row & col 순서 주의
			}
			k++;
		}
	}
    
}

Map으로 탐색 시간을 단축하는 것도 중요하지만, 배열의 증가 규칙을 찾는 것이 특히나 어려운 문제였다. 아직 시뮬레이션 문제나 규칙 찾기 문제에 약하기 때문에 이러한 문제를 더욱 많이 푸는 것이 좋겠다.

 

참고한 블로그

https://velog.io/@gkdud583/JAVA-SWEA-1493-수의-새로운-연산

 

git 링크

(git 링크 첨부)

문제 풀이 날짜: 2023.07.25
포스트 작성일: 2023.07.25

 

* 학습 목적으로 작성하는 글입니다. 풀이가 정석적이지 못할 수도 있습니다.

 

문제 출처

Software Expert Academy 1954번: 달팽이 숫자 (D2)

 

키워드

배열 다루기, 구현

 

풀이 접근법

  • 달팽이 껍질 처럼 빙글빙글 도는 모양으로 배열 값을 대입해야 한다. 
  • 배열이 꺾이는 부분은 dx, dy로 구현한다. 이동 경로가 꺾이는 부분에서 델타 값을 변경하면 이를 구현할 수 있다.
  • 이동 경로가 꺾여야 하는 기준은 두 가지로 잡는다.
    • 1. 이미 0이 아닌 다른 값이 채워져 있다면 경로 변경
    • 2. 배열의 범위를 넘어선 곳을 탐색하려고 했다면 경로 변경

 

코드

import java.io.*;

public class Main {
	
	private static int[] dx = {1, 0, -1, 0}; //→↓←↑
	private static int[] dy = {0, 1, 0, -1};
	
	private static int N;
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int T = Integer.parseInt(br.readLine());

		StringBuilder sb = new StringBuilder();
		for(int test_case = 1; test_case <= T; test_case++) {
            N = Integer.parseInt(br.readLine());
            
            int[][] matrix = new int[N][N];
            
            int idx = 0;
            int r = 0;
            int c = 0;
            for(int k = 1; k <= N * N;) {  
            	int nr = 0;
                int nc = 0;
            	if(matrix[r][c] != 0) { //다음 위치로 인덱스 변경
            		nr = r + dy[idx];
            		nc = c + dx[idx];
            	}   
            	
            	//이미 값이 채워져 있거나, 배열 범위를 넘어섰다면
            	if(!isValidIndex(nr, nc) || matrix[nr][nc] != 0) { //방향 전환
            		idx++;
            		if(idx >= dx.length) {
            			idx = idx % dx.length;
            		}
            		continue;
            	}
            	
            	matrix[nr][nc] = k++;
            	r = nr;
            	c = nc;
            }
            
            sb.append("#").append(test_case).append("\n");
			for(int i = 0; i < N; i++) {
				for(int j = 0; j < N; j++) {
					sb.append(matrix[i][j]).append(" ");
	            }
				sb.append("\n");
            }
		}
		
		System.out.print(sb.toString());
	}

	private static boolean isValidIndex(int nr, int nc) {
		return (nr > -1 && nc > -1 && nr < N && nc < N);
	}

}

 

 

git 링크

(git 링크 첨부)

문제 풀이 날짜: 2023.07.24
포스트 작성일: 2023.07.24

 

* 학습 목적으로 작성하는 글입니다. 풀이가 정석적이지 못할 수도 있습니다.

 

문제 출처

Software Expert Academy 11979번: 어디에 단어가 들어갈 수 있을까 (D2) 

 

키워드

구현, 완전 탐색

 

풀이 접근법

  • 주어진 단어가 들어가려면 1로 된 칸이 연속적으로 존재해야 한다.
  • 어떻게 해야 칸이 연속적으로 K개만 존재하도록 만들 수 있을까를 고민해봐야 한다.

 

코드

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

public class SWEA1979 {

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

        StringBuilder sb = new StringBuilder();
		for(int test_case = 1; test_case <= T; test_case++) {
            String[] temp = br.readLine().split(" ");
			
            int N = Integer.parseInt(temp[0]);
            int K = Integer.parseInt(temp[1]);
            
            int[][] matrix = new int[N][N];
           	 
            StringTokenizer st;
            for(int i = 0; i < N; i++) {
                st = new StringTokenizer(br.readLine(), " ");
            	for(int j = 0; j < N; j++) {
                    matrix[i][j] = Integer.parseInt(st.nextToken());
                }
            }
            
            int answer = 0;
            
            int curr = 0;
            int prev = 0;
            int len = 0;
            
            for(int i = 0; i < N; i++) {
            	curr = 0;
            	prev = 0;
            	len = 1;
            	for(int j = 0; j < N; j++) {
            		curr = matrix[i][j];
                    if(curr == 1) {
                    	if(prev == 1) {
                    		len++;
                    	}
                    	else { //prev == 1
                    		prev = 1;
                    	}
                    }
                    else if(curr != 1) {
                    	if(len == K) {
                    		answer++;
                    	}
                    	
                    	prev = 0;
                    	len = 1;
                    }
                }
            	
            	if(len == K) {
            		answer++;
            	}
            }
            
            for(int j = 0; j < N; j++) {
            	curr = 0;
            	prev = 0;
            	len = 1;
            	for(int i = 0; i < N; i++) {
            		curr = matrix[i][j];
                    if(curr == 1) {
                    	if(prev == 1) {
                    		len++;
                    	}
                    	else { //prev == 1
                    		prev = 1;
                    	}
                    }
                    else if(curr != 1) {
                    	if(len == K) {
                    		answer++;
                    	}
                    	
                    	prev = 0;
                    	len = 1;
                    }
                }
            	
            	if(len == K) {
            		answer++;
            	}
            }
            
            sb.append("#").append(test_case).append(" ").append(answer).append("\n");
		}
        
        System.out.print(sb);
	}

}

좀처럼 깔끔한 풀이가 나오지 않는 문제이다. 배열의 가로와 세로를 각각 탐색할 때 필연적으로 중복된 코드가 발생하는데, 이것을 모듈로 분리하고 리팩토링 하는 것이 관건일 듯하다.

 

git 링크

https://github.com/jinju9553/23-CodingTest/blob/main/second/week4/day2/SWEA1979.java

 

문제 풀이 날짜: 2023.05.11
포스트 작성일: 2023.05.25

 

* 학습 목적으로 작성하는 글입니다. 풀이가 정석적이지 못할 수도 있습니다.

 

문제 출처

백준 온라인 저지 10971번: 외판원 순회2 (실버2) (문제 링크)

 

키워드

DFS, 백트랙킹

 

풀이 접근법

  • 실버2의 외판원 순회는 입력이 2 ≤ N ≤ 10 이기 때문에 백트랙킹으로 풀이 가능하다.
    • 골드1 등급에도 외판원 순회1이 있는데, 해당 문제는 입력이 2 ≤ N ≤ 16이므로 백트랙킹이 아닌 다른 방식으로 풀이해야 한다.
  • 어느 한 도시에서 출발해 N개의 도시를 모두 거쳐 다시 원래의 도시로 돌아오는 순회 여행 경로 ⇒ DFS로 모든 도시를 방문하며 비용을 누적시키고, 마지막 도시로부터 출발지로 돌아가는 비용까지 누적시킨다. 
    • 경로는 각 마을을 출발지로 삼아서 한 번씩 계산한다.
    • DFS에서 탐색할 후보는 '현재 위치 u에서 갈 수 있는 모든 v들'이다.
  • 또, 마지막 마을에서 첫 번째 마을로 곧바로 올 수 있는 경로는 최소 한 가지 이상 존재하도록 케이스가 구성되어 있다.
    • 그러나 (DFS의 깊이 == N)에 도달했을 때 반드시 첫 번째 마을로 돌아오는 경로가 있다는 것을 보장할 수 없다. 따라서 해당 마을에서 첫 번째 마을로 돌아오는 경로가 있는지를 반드시 체크하고 비용을 더해주어야 한다.
    • 각 마을마다로 시작점을 달리해서 출발해야 하는데, 출발하는 마을의 visited를 true로 바꾸고 DFS를 시작해야 한다는 점도 주의하자. 

 

코드

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

public class Q10971 {

	private static boolean[] visited;
	private static int[][] weight;
	private static int answer = Integer.MAX_VALUE;
	
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

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

		visited = new boolean[N];
		weight = new int[N][N];

		StringTokenizer st;
		for (int i = 0; i < N; i++) {
			st = new StringTokenizer(br.readLine(), " ");
			for (int j = 0; j < N; j++) {
				weight[i][j] = Integer.parseInt(st.nextToken());
			}
		}
		
		for(int i = 0; i < N; i++) {
			visited[i] = true; //여기서 시작하기 때문에 depth = 1로 초기화 
			
			DFS(i, i, N, 1, 0);

			visited = new boolean[N];
		}
		
		System.out.print(answer);
	}
	
	public static void DFS(int start, int curr, int N, int depth, int length) {
		
		if(N == depth) {
			if(weight[curr][start] != 0) {
				answer = Math.min(length + weight[curr][start], answer);
			}
			return;
		}
		
		for(int i = 0; i < N; i++) {
			if(!visited[i] && weight[curr][i] != 0) {
				visited[i] = true;
				
				DFS(start, i, N, depth + 1, length + weight[curr][i]);
				
				visited[i] = false;
			}
		}
		
		return;
	}

}

백트랙킹 문제의 응용판 중 하나로, 시작점을 바꿔가며 각 마을마다 DFS를 해야한다는 점을 기억하고 전체 비용을 계산할 때 첫 번째 마을로 되돌아가야 한다는 조건만 빠트리지 않으면 어렵지 않게 풀 수 있다.

 

git 링크

https://github.com/jinju9553/23-CodingTest/blob/main/week7/day6/Q10971.java

문제 풀이 날짜: 2023.05.17
포스트 작성일: 2023.05.25

 

* 학습 목적으로 작성하는 글입니다. 풀이가 정석적이지 못할 수도 있습니다.

 

문제 출처

Software Expert Academy 1859번: 백만 장자 프로젝트 (D2) (문제 링크)

 

키워드

그리디

 

풀이 접근법

  • 배열의 앞에서부터 뒤로 가는 게 아니라 뒤에서부터 앞으로 가면서 계산하는 발상이 필요하다.
    • 앞에서부터 계산하면 "1 2 3 4 2 6 1 1 1 1" 이란 테스트 케이스에서 반례가 생긴다. 중간에 가격이 치솟는 시점(4일)이 있더라도 6일까지 사재기 한 다음에 6일에 모두 팔아야 한다.
  • 즉, 앞에서부터 사재기 하다가 값이 떨어지기 직전에 파는 것이 아니라, 맨 뒤에서부터 보았을 때 금액이 치솟는 시점마다 그 가격보다 저렴했던 전날들의 마진(maxCost - cost[i])을 모두 누적시킨다.
    • 예: 1, 1, 5, 1, 4에서는 가격이 4일 때를 기점으로 가격이 4보다 작은 날들의 마진을 구한다. 가격이 5일 때 기준으로 그 전 날들의 마진을 구한다.

 

코드

import java.util.*;

public class SWEA1859 {

	public static void main(String[] args) {
		@SuppressWarnings("resource")
		Scanner sc = new Scanner(System.in);
		
		int T = sc.nextInt();

		StringBuilder sb = new StringBuilder();
		for(int test_case = 1; test_case <= T; test_case++) {
			int N = sc.nextInt();

			int[] cost = new int[N];
			for(int i = 0; i < N; i++) { //배열에 값 할당 
				cost[i] = sc.nextInt();
			}
			
			int max = cost[N - 1];
			long result = 0;
			for(int i = N - 2; i >= 0; i--) {
				if(cost[i] < max) { //최대 금액보다 i일의 시세(cost[i])가 낮다면
					result += (max - cost[i]); //i일의 마진을 누적시킨다.
				}
				else { //최대 금액이 발견되었다면 갱신
					max = cost[i];
				}
			}
			
			sb.append("#" + test_case + " ").append(result).append('\n');
		}
		
		System.out.print(sb);
	}

}

역발상이 조금 필요한 그리디 문제였다. 앞에서부터 계산하면 영락없이 반례에 걸려버리는데, 이걸 고치는 과정에서 조금 헷갈려서 다른 분들의 풀이를 참고하여 코드를 작성하였다. 그리디 문제를 풀 때에는 뒤에서부터 계산하는 응용력이 필요할 것 같다. 

 

git 링크

https://github.com/jinju9553/23-CodingTest/blob/main/week8/day4/SWEA1859.java

 

참고한 블로그 링크

https://dundung.tistory.com/119

+ Recent posts