728x90

문제

 

15659번: 연산자 끼워넣기 (3)

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

www.acmicpc.net

 

접근 방식

기존의 연산자 끼워넣기 1, 2번 시리즈와 비슷하지만 연산자의 우선순위를 적용해야 해서

선택한 연산자들을 배열에 기록해 두었다 나중에 한 번에 연산을 해줘야 한다.

 

중간중간에 처리해서 빠르게 계산을 해보려 했는데 예제들은 통과가 되지만

제출하면 도무지 1 퍼에서 올라가질 않길래 포기하고 한 번에 연산을 해줬다.

 

기존 방식대로 각각의 연산자들을 선택할 수 있을 때 선택하는 방식으로 진행하면서

n-1개의 연산자를 선택했을 때 스택을 활용해서 우선순위대로 처리해 주면 된다.

 

덧셈과 뺄셈은 스택에 계속해서 쌓아두다가

곱셈과 나눗셈일 때 이전 값을 꺼내서 계산한 후에 스택에 결과를 넣어주고

모든 연산자를 다 처리한 후에 스택의 값들을 모두 꺼내면서 더해주면 된다.

 

풀이

public class Main {

	static int n, plus, minus, multi, div, max = -1000000001, min = 1000000001;
	static int[] arr;
	static char[] ops;

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

		n = Integer.parseInt(br.readLine());
		arr = new int[n];
		ops = new char[n - 1];

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

		for (int i = 0; i < n; i++) arr[i] = Integer.parseInt(st.nextToken());

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

		plus = Integer.parseInt(st.nextToken());
		minus = Integer.parseInt(st.nextToken());
		multi = Integer.parseInt(st.nextToken());
		div = Integer.parseInt(st.nextToken());

		solve(0);

		System.out.println(max + "\n" + min);
	}

	static void solve(int size) {
		if (size == n) {
			calcurate();
			return;
		}

		if (size == 0) {
			solve(size + 1);
		} else {
			if (plus != 0) {
				plus--;
				ops[size - 1] = '+';
				solve(size + 1);
				plus++;
			}

			if (minus != 0) {
				minus--;
				ops[size - 1] = '-';
				solve(size + 1);
				minus++;
			}

			if (multi != 0) {
				multi--;
				ops[size - 1] = '*';
				solve(size + 1);
				multi++;
			}

			if (div != 0) {
				div--;
				ops[size - 1] = '/';
				solve(size + 1);
				div++;
			}
		}
	}

	static void calcurate() {
		int result = 0;

		Stack<Integer> s = new Stack<>();

		s.push(arr[0]);

		for (int i = 0; i < n - 1; i++) {
			switch (ops[i]) {
				case '+': {
					s.push(arr[i + 1]);
					break;
				}
				case '-': {
					s.push(arr[i + 1] * -1);
					break;
				}
				case '*': {
					s.push(s.pop() * arr[i + 1]);
					break;
				}
				default: {
					s.push(s.pop() / arr[i + 1]);
					break;
				}
			}
		}

		while (!s.isEmpty()) {
			result += s.pop();
		}

		max = Math.max(max, result);
		min = Math.min(min, result);
	}
}

 

728x90

문제

 

1759번: 암호 만들기

첫째 줄에 두 정수 L, C가 주어진다. (3 ≤ L ≤ C ≤ 15) 다음 줄에는 C개의 문자들이 공백으로 구분되어 주어진다. 주어지는 문자들은 알파벳 소문자이며, 중복되는 것은 없다.

www.acmicpc.net

 

접근 방식

백트래킹으로 조건을 체크해가며 암호를 만들어주는 문제다.

 

암호는 다음과 같은 조건을 체크해줘야한다.

  • 암호는 사전순으로 오름차순으로 정렬되어야 한다.
  • 모음은 하나 이상, 자음은 2개 이상 포함되어야 한다.

 

그래서 입력으로 받은 문자 배열을 사전순으로 정렬한 후에

백트래킹으로 조합을 해주면서 모음과 자음을 카운트하여

파라미터로 들고 다니면 된다.

 

계속해서 조합을 하다 원하는 암호의 길이를 만족하면

모음과 자음수가 조건을 만족하는지 체크하고

만족하면 출력해주고 그렇지 않다면 넘어가면 된다.

 

풀이

public class Main {

	static int l, c;
	static char[] chars, password;
	static StringBuilder sb = new StringBuilder();

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

		l = Integer.parseInt(st.nextToken());
		c = Integer.parseInt(st.nextToken());

		st = new StringTokenizer(br.readLine());
		password = new char[l];
		chars = new char[c];

		for (int i = 0; i < c; i++) {
			chars[i] = st.nextToken().charAt(0);
		}

		Arrays.sort(chars);

		solve(0, 0, 0, 0);

		System.out.println(sb);
	}

	static void solve(int size, int start, int vo, int co) {
		if (size == l) {
			if (vo > 0 && co > 1) sb.append(new String(password)).append("\n");
			return;
		}

		for (int i = start; i < c; i++) {
			password[size] = chars[i];

			boolean isVowel = isVowel(chars[i]);
			int nv = isVowel ? vo + 1 : vo;
			int nc = !isVowel ? co + 1 : co;

			solve(size + 1, i + 1, nv, nc);
		}
	}

	static boolean isVowel(char c) {
		return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
	}
}

 

728x90

문제

 

15658번: 연산자 끼워넣기 (2)

첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1보다 크거나 같고, 4N보다 작거나 같은 4개의 정수가 주어지는데, 차례대

www.acmicpc.net

 

 

접근 방식

연산자 끼워넣기 1번이랑 다른 점은

주어진 연산자의 수가 주어진 수들보다 많을 수 있다는 것인데

기존 방식 그대로 풀어도 딱히 문제는 없다.

 

기존의 백트래킹 방식 자체가 애초에

모든 연산자를 다 사용해보기 때문에 수의 개수와

맞지 않아도 모두 조합해볼 수 있다.

 

풀이

class Main {
    static int n, max = -1000000001, min = Integer.MAX_VALUE;
	static int plus, minus, multi, div;
	static int[] nums;
	static boolean[] isUsed;

	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());
		nums = new int[n];
		isUsed = new boolean[n];

		StringTokenizer st = new StringTokenizer(br.readLine());
		for (int i = 0; i < n; i++) nums[i] = Integer.parseInt(st.nextToken());

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

		plus = Integer.parseInt(st.nextToken());
		minus = Integer.parseInt(st.nextToken());
		multi = Integer.parseInt(st.nextToken());
		div = Integer.parseInt(st.nextToken());

		solve(0, 0);

		sb.append(max).append("\n").append(min);
		System.out.println(sb);
	}

	static void solve(int size, int sum) {
		if (size == n) {
			min = Math.min(min, sum);
			max = Math.max(max, sum);
			return;
		}

		if (size == 0) {
			solve(size + 1, sum + nums[size]);
		} else {
			if (plus > 0) {
				plus--;
				solve(size + 1, sum + nums[size]);
				plus++;
			}

			if (minus > 0) {
				minus--;
				solve(size + 1, sum - nums[size]);
				minus++;
			}

			if (multi > 0) {
				multi--;
				solve(size + 1, sum * nums[size]);
				multi++;
			}

			if (div > 0) {
				div--;
				solve(size + 1, sum / nums[size]);
				div++;
			}
		}
	}
}

 

728x90

개요

토비의 스프링을 다시 읽으며, 개념을 자세히 정리하기보다는 면접에 나올 만한 부분을 질의응답 형식으로 정리하려고 합니다.

책을 읽는 동안 지속적으로 업데이트되는 게시글입니다.

친절하게 설명을 해주시기 위해 분량이 많아진 것이라 생각하기 때문에, 입문자도 한 번 정도는 읽어보는 것도 좋다고 생각합니다.

내용의 순서는 가나다 순이 아닌 책의 흐름에 따라 진행됩니다.

전체 글 목록은 아래 페이지에서 확인할 수 있습니다.

토비의 스프링으로 면접 준비하기

 

 

질문 및 답변

1권의 3장인 템플릿 챕터에 대한 질답 목록입니다.

 

템플릿이란

 변경이 거의 일어나지 않으며 일정한 패턴으로 유지되는 특성을 가진 부분을 자유롭게 변경되는 성질을 가진 부분으로부터 독립시켜 효과적으로 활용할 수 있도록 하는 방법이다. 즉, 고정된 작업 흐름을 가진 코드를 재사용한다는 것이다.

 

템플릿의 장점

 변하지 않지만 많은 곳에서 반복적으로 사용되는 코드와 로직에 따라 자주 확장되고 변하는 코드를 분리할 때 유용하다.

 

내부 클래스

클래스의 내부에 선언된 클래스로 주로 속해있는 클래스와 밀접한 관계에 있거나 특정 클래스나 메서드에서만 사용되는 경우 활용된다. 밀접한 관계의 클래스들을 그룹화할 수 있고, 외부로부터 강력하게 캡슐화를 할 수 있으며, 가독성을 향상할 수 있다.

 

익명 클래스

 말 그대로 이름이 없는 클래스로 나중에 다시 사용될 여지가 없는 경우 사용하는 클래스다. 일회용 클래스를 생성하지 않고 익명 클래스를 사용해 클래스의 선언과 오브젝트 생성을 합쳐 코드를 간결하게 줄일 수 있다.

 

콜백

 실행되는 것을 목적으로 다른 오브젝트의 메서드에 전달되는 오브젝트이다. 값을 참조하는 것이 목적이 아닌 특정 로직을 담은 메서드의 실행을 위해 파라미터로 전달되는 오브젝트라고 볼 수 있다.

 

템플릿/콜백 패턴

 

728x90

문제

 

2529번: 부등호

두 종류의 부등호 기호 ‘<’와 ‘>’가 k개 나열된 순서열 A가 있다. 우리는 이 부등호 기호 앞뒤에 서로 다른 한 자릿수 숫자를 넣어서 모든 부등호 관계를 만족시키려고 한다. 예를 들어, 제시

www.acmicpc.net

 

접근 방식

백트래킹을 사용해 0 ~ 9까지의 수들을 한 번만 사용하며 조합해가면서

주어진 부등호의 순서와 대소관계가 일치하는지 확인해주면 된다.

 

첫 번째 고른 숫자를 제외하고 이후에는 부등호와 이전 숫자와 현재 숫자가

일치하는지 확인해주며 진행하면 된다.

 

모든 숫자를 고른 후에 부등호를 비교하는 것도 상관 없지만

매번 숫자를 고를 때마다 확인하면 중간에 맞지 않는 경우에 멈출 수 있어서 좋다.

 

k가 9인 경우 최대 값이 9876543210이기 때문에 int 타입의 변수로 값을 다루면

오버플로우나 예외가 발생할 수 있으니 이 부분만 조심하면 된다.

 

풀이

public class Main {

	static int k, end;
	static long max = 0, min = Long.MAX_VALUE;
	static String maxStr = "", minStr = "";
	static String[] sign;
	static char[] nums;
	static boolean[] isUsedSign;
	static boolean[] isUsedNum = new boolean[10];

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

		k = Integer.parseInt(br.readLine());
		end = k + 1;

		sign = new String[k];
		isUsedSign = new boolean[k];
		nums = new char[end];

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

		for (int i = 0; i < k; i++) {
			sign[i] = st.nextToken();
		}

		solve(0);

		System.out.println(maxStr + "\n" + minStr);
	}

	static void solve(int size) {
		if (size == end) {
			String s = new String(nums);
			long num = Long.parseLong(s);
			if (max < num) {
				maxStr = s;
				max = num;
			}
			if (min > num) {
				minStr = s;
				min = num;
			}
			return;
		}

		for (int i = 0; i < 10; i++) {
			if (isUsedNum[i]) continue;

			if (size == 0) {
				isUsedNum[i] = true;
				nums[size] = (char)(i + '0');
				solve(size + 1);
				isUsedNum[i] = false;
			}
			else {
				if (isSatisfied(size, i)) {
					isUsedNum[i] = true;
					nums[size] = (char)(i + '0');
					solve(size + 1);
					isUsedNum[i] = false;
				}
			}
		}
	}

	static boolean isSatisfied(int size, int y) {
		int x = nums[size - 1] - '0';
		return (sign[size - 1].equals(">") && x > y) || (sign[size - 1].equals("<") && x < y);
	}
}

 

728x90

개요

토비의 스프링을 다시 읽으며, 개념을 자세히 정리하기보다는 면접에 나올 만한 부분을 질의응답 형식으로 정리하려고 합니다.

책을 읽는 동안 지속적으로 업데이트되는 게시글입니다.

친절하게 설명을 해주시기 위해 분량이 많아진 것이라 생각하기 때문에, 입문자도 한 번 정도는 읽어보는 것도 좋다고 생각합니다.

내용의 순서는 가나다 순이 아닌 책의 흐름에 따라 진행됩니다.

전체 글 목록은 아래 페이지에서 확인할 수 있습니다.

토비의 스프링으로 면접 준비하기

 

 

질문 및 답변

1권의 2장인 테스트 챕터에 대한 질답 목록입니다.

테스트의 가치와 장점, 활용 전략, 스프링과의 관계에 대해 질답입니다.

 

테스트의 유용성

 테스트는  코드가 예상하고 의도한 대로 정확히 동작하는지 확인하여 확신할 수 있게 해주는 작업으로, 이를 통해 설계나 코드의 결함을 발견하고 제거할 수 있다.

 

웹을 통한 테스트 방식의 문제점

 관련된 모든 기능을 계층별로 구현한 후에 웹을 통해 요청을 보내 예상대로 동작하는지 확인해야 하기 때문에 도중에 테스트가 불가능하고, 모든 기능을 구현한 후에 문제를 발견하면 어느 부분에서 문제가 발생했는지 파악하기 힘들다. 이는 결국 하나의 테스트를 위해 참여하는 클래스와 코드가 많기 때문에 테스트의 결과가 다른 계층의 코드와 컴포넌트 등에 영향을 많이 받고 번거로움이 생기는 문제로 이어진다.

 

테스트의 단위가 작아야 하는 이유

 많은 것을 한 번에 테스트를 하기 위해서는 과정도 복잡해지고 문제의 원인을 찾기 힘들어진다. 따라서 테스트도 관심사의 분리를 적용해 관심이 다른 테스트는 분리하여 작은 단위로 테스트하는 것이 좋다. 이렇게 작은 단위의 테스트는 큰 단위의 테스트를 진행할 때 발생한 문제의 원인을 찾기도 더욱 쉽게 만들어 준다.

 

단위 테스트란

 작은 단위의 코드에 대해 테스트를 수행하는 것으로 정확한 단위나 크기와 범위가 정해진 것은 아니지만 하나의 관심에 집중해서 효율적으로 테스트할 수 있는 범위에 대한 테스트다.

 

테스트 주도 개발(TDD, Test Driven Development)

 만들고자 하는 기능의 내용과 코드를 검증할 수 있는 테스트 코드를 먼저 작성한 후에 테스트가 성공할 수 있게 코드를 작성하고 리팩토링을 거치는 하나의 사이클을 반복하며 기능을 완성시켜 나가는 방식의 개발 방법이다.

 

TDD의 장점

 테스트를 생성한 후에 코드를 완성해 나가기 때문에 테스트를 생략하지 않고 꼼꼼하게 개발을 진행할 수 있고, 테스트를 작성하는 시간과 로직 작성의 시간 간격이 짧아 피드백을 빠르게 받을 수 있다. 또한 작은 기능 단위로 테스트를 진행하며 기능을 완성하기 때문에 자연스럽게 단위 테스트를 수행할 수 있다.

 

Fixture

 테스트를 수행하는 데 필요한 정보다 오브젝트이다. 일반적으로 테스트에서 반복적으로 사용되기 때문에 중복을 제거하기 위해 추출해서 사용한다.

 

테스트와 애플리케이션 컨텍스트의 관계

 애플리케이션 컨텍스트가 생성될 때는 모든 싱글톤 빈 오브젝트를 초기화하기 때문에 빈이 많아지고 복잡한 애플리케이션일수록 컨텍스트 생성에 큰 비용이 소모된다. 애플리케이션 컨텍스트는 초기화 이후에는 내부 상태가 바뀌는 일이 거의 없고, 빈은 싱글톤이라 상태를 가지지 않기 때문에 여러 테스트가 공유해서 사용해도 문제가 없다.

 스프링은 이런 문제를 해결하기 위해 환경이 같은 테스트가 하나의 애플리케이션 컨텍스트를 공유해서 사용할 수 있게 지원한다. 

 

DI는 왜 필요한가

 구현 클래스가 하나이더라도 인터페이스를 통한 DI는 사용해야 한다. 소프트웨어 개발에서 변하지 않는 것은 없고, 부가적인 기능을 추가하기 쉬워지며, 가능한 작은 단위의 대상에 대해 독립적으로 만들어지고 실행되게 하기 때문에 테스트에 유용하다.

 

스프링에서 테스트를 하기 위해서는 컨텍스트가 필수일까?

 스프링 API를 직접 사용하지 않는 코드를 테스트한다면 굳이 무겁게 컨텍스트를 생성하거나 사용할 필요가 없다.

728x90

개요

토비의 스프링을 다시 읽으며, 개념을 자세히 정리하기보다는 면접에 나올 만한 부분을 질의응답 형식으로 정리하려고 합니다.

친절하게 설명을 해주시기 위해 분량이 많아진 것이라 생각하기 때문에, 입문자도 한 번 정도는 읽어보는 것도 좋다고 생각합니다.

내용의 순서는 가나다 순이 아닌 책의 흐름에 따라 진행됩니다.

 

 

목차

[토비의 스프링으로 면접 준비하기] 1장:오브젝트와 의존관계

[토비의 스프링으로 면접 준비하기] 2장:테스트

[토비의 스프링으로 면접 준비하기] 3장:템플릿

 

 

728x90

문제

 

10819번: 차이를 최대로

첫째 줄에 N (3 ≤ N ≤ 8)이 주어진다. 둘째 줄에는 배열 A에 들어있는 정수가 주어진다. 배열에 들어있는 정수는 -100보다 크거나 같고, 100보다 작거나 같다.

www.acmicpc.net

 

접근 방식

쉬운 조합 문제인데 문제를 잘못 봐서 헤맸다.

 

abs(num[0] - num[1]) + abs(num[2] - num[3]) + ... +  abs(num[n - 2] - num[n -1]) 가 아니라

abs(num[0] - num[1]) + abs(num[1] - num[2]) + ... +  abs(num[n - 2] - num[n -1]) 인 부분을 조심해야 한다.

 

이 부분만 조심하면서 재귀 호출 시 파라미터로 이전까지의 계산을 넘겨주면서

백트래킹을 이용해 조합만 해주면 된다.

 

이전에 고른 수를 알고 있어야 하기 때문에

추가로 배열을 사용해 수를 기록해두면 편리하다.

 

풀이

public class Main {

	static int n, max = -1000000;
	static int[] arr, nums;
	static boolean[] isUsed;

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

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

		arr = new int[n];
		nums = new int[n];
		isUsed = new boolean[n];

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

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

		solve(0, 0);

		System.out.println(max);
	}

	static void solve(int cnt, int sum) {
		if (cnt == n) {
			max = Math.max(max, sum);
			return;
		}

		for (int i = 0; i < n; i++) {
			if (isUsed[i]) continue;
			isUsed[i] = true;
			nums[cnt] = arr[i];
			if (cnt == 0) solve(cnt + 1, sum);
			else solve(cnt + 1, sum + Math.abs(nums[cnt - 1] - nums[cnt]));
			isUsed[i] = false;
		}
	}
}

 

+ Recent posts