주어진 예시를 보면 줄 세우기 과정은 아래와 같다.

3 7 5 2 6 1 4

3 7 4 5 2 6 1

3 4 5 2 6 1 7

1 3 4 5 2 6 7

 

즉, 제자리에 있는 아이들의 번호는 아래와 같다. == LIS

3 7 5 2 6 1 4

 

import java.util.*;

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

		int n = in.nextInt();
		int child[] = new int[n + 1];
		int ans[] = new int[n + 1];
		for (int i = 1; i <= n; i++) {
			child[i] = in.nextInt();
		}
		ans[1] = 1;

		for (int i = 2; i <= n; i++) {
			int max = 0;
			for (int j = 0; j < i; j++) {
				if (child[i] > child[j]) {
					max = Math.max(max, ans[j]);
				}
			}
			ans[i] = max + 1;
		}

		Arrays.sort(ans);
		System.out.println(n - ans[n]);
	}
}

문제 조건

  • 1 ≤ 신이 좋아하는 문자열의 길이 ≤ 5
  • 1 ≤ K ≤ 1,000, K는 자연수이다.

 

처음에는 문자열 길이만 보고 거의 완탐으로 문제를 풀었는데, 시간 초과가 났다

DP인가 했지만 이미 지나 왔던 칸들을 다시 방문하는 것은 허용한다고 했기 때문에 불가능 하고,

갈 수 있는 방향을 모두 탐색하는 대신 map으로 다음 문자열과 같은 곳만 탐색하도록 하는 방법을 사용했다.

이를 적용한 `틀린` 코드는 아래와 같다. => 이렇게 풀면 최악일 때 10* 10 * 1000 * 5^8 이 나온다

import java.util.*;

public class Main {

	private static int dr[] = { -1, -1, 0, 1, 1, 1, 0, -1 };
	private static int dc[] = { 0, -1, -1, -1, 0, 1, 1, 1 };

	private static int sum = 0;

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

		int row = in.nextInt();
		int col = in.nextInt();
		int testcase = in.nextInt();
		char[][] alphabets = new char[row + 1][col + 1];
		HashMap<Character, HashSet<Position>> map[][] = new HashMap[row + 1][col + 1];
		for (int i = 1; i <= row; i++) {
			String rowInput = in.next();
			for (int j = 1; j <= col; j++) {
				alphabets[i][j] = rowInput.charAt(j - 1);
				map[i][j] = new HashMap<Character, HashSet<Position>>();
			}
		}

		for (int i = 1; i <= row; i++) {
			for (int j = 1; j <= col; j++) {
				for (int d = 0; d < 8; d++) {
					int newRow = i + dr[d];
					int newCol = j + dc[d];

					if (newRow < 1) {
						newRow = row;
					}
					if (newCol < 1) {
						newCol = col;
					}
					if (newRow > row) {
						newRow = 1;
					}
					if (newCol > col) {
						newCol = 1;
					}

					char ch = alphabets[newRow][newCol];

					if (map[i][j] == null) {
						map[i][j] = new HashMap<Character, HashSet<Position>>();
					}
					if (!map[i][j].containsKey(ch)) {
						map[i][j].put(ch, new HashSet<>());
					}
					map[i][j].get(ch).add(new Position(newRow, newCol));
				}
			}
		}

		for (int t = 0; t < testcase; t++) {
			char[] favorite = in.next().toCharArray();
			int length = favorite.length;
			sum = 0;

			for (int i = 1; i <= row; i++) {
				for (int j = 1; j <= col; j++) {
					if (favorite[0] == alphabets[i][j]) {
						findAll(1, i, j, favorite, length, row, col, alphabets, map);
					}
				}
			}

			System.out.println(sum);
		}
	}

	private static void findAll(int index, int row, int col, char[] target, int targetLength, int maxRow, int maxCol,
			char[][] alphabets, HashMap<Character, HashSet<Position>> map[][]) {
		if (index == targetLength) {
			sum++;
			return;
		}

		HashSet<Position> set = map[row][col].get(target[index]);
		if (set != null) {

			for (Position p : set) {
				int newRow = p.row;
				int newCol = p.col;

				findAll(index + 1, newRow, newCol, target, targetLength, maxRow, maxCol, alphabets, map);
			}
		}
	}
}

class Position {
	int row, col;

	public Position(int row, int col) {
		this.row = row;
		this.col = col;
	}
}

 

 

 

그래서 어차피 완전 탐색은 한 번은 꼭 해야 할텐데 방문했을 때 생긴 문자열의 수를 각각 구해서 저장한 뒤,

문자열을 입력받을 때마다 바로 리턴하는 방식으로 바꾸기로 했다. 이렇게 될 경우 시간 복잡도는 O(10* 10 * 5^8) 으로 줄어들게 된다.

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

public class Main {

	private static final int dr[] = { -1, -1, 0, 1, 1, 1, 0, -1 };
	private static final int dc[] = { 0, -1, -1, -1, 0, 1, 1, 1 };

	private static final int MAX_LENGTH = 5;

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

		int row = Integer.parseInt(st.nextToken());
		int col = Integer.parseInt(st.nextToken());
		int testcase = Integer.parseInt(st.nextToken());
		char[][] alphabets = new char[row + 1][col + 1];
		HashMap<String, Integer> map = new HashMap<>();
		for (int i = 1; i <= row; i++) {
			String rowInput = br.readLine();
			for (int j = 1; j <= col; j++) {
				alphabets[i][j] = rowInput.charAt(j - 1);
			}
		}

		for (int i = 1; i <= row; i++) {
			for (int j = 1; j <= col; j++) {
				String createdString = Character.toString(alphabets[i][j]);
				map.put(createdString, map.getOrDefault(createdString, 0) + 1);
				findAll(1, i, j, row, col, map, createdString, alphabets);
			}
		}

		for (int t = 0; t < testcase; t++) {
			String favorite = br.readLine();
			System.out.println(map.getOrDefault(favorite, 0));
		}
	}

	private static void findAll(int index, int row, int col, int maxRow, int maxCol, HashMap<String, Integer> map,
			String createdString, char[][] alphabets) {
		if (index == MAX_LENGTH) {
			return;
		}

		for (int d = 0; d < 8; d++) {
			int newRow = row + dr[d];
			int newCol = col + dc[d];

			if (newRow < 1) {
				newRow = maxRow;
			}
			if (newCol < 1) {
				newCol = maxCol;
			}
			if (newRow > maxRow) {
				newRow = 1;
			}
			if (newCol > maxCol) {
				newCol = 1;
			}

			String newCreatedString = createdString + alphabets[newRow][newCol];
			map.put(newCreatedString, map.getOrDefault(newCreatedString, 0) + 1);
			findAll(index + 1, newRow, newCol, maxRow, maxCol, map, newCreatedString, alphabets);
		}
	}
}

 

 

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

boj 2631: 줄세우기  (0) 2021.04.01
boj 20165: 인내의 도미노 장인 호석  (0) 2021.03.24
boj 20164: 홀수 홀릭 호석  (0) 2021.03.24
boj 11663: 선분 위의 점  (0) 2021.03.17
boj 19637: IF문 좀 대신 써줘  (0) 2021.03.17

뭔가 문제가 삼성느낌이 난다...

문제 풀이 코드는 아래와 같다. 코드가 넘 긴 느낌이 있긴 하지만... 시키는 대로만 하면 풀 수 있다

import java.util.*;

public class Main {
	private static int STAND = 0;
	private static int FALL = 1;

	private static int[][] before;

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

		int R = in.nextInt();
		int C = in.nextInt();
		int round = in.nextInt();

		int mat[][] = new int[R][C];
		for (int i = 0; i < R; i++) {
			for (int j = 0; j < C; j++) {
				mat[i][j] = in.nextInt();
			}
		}

		before = new int[R][C]; // 0: 서 있음, 1: 넘어짐
		int score = 0;
		for (int r = 0; r < round; r++) {
			int attackR = in.nextInt() - 1;
			int attackC = in.nextInt() - 1;
			Direction direction = Direction.valueOf(in.next());

			int defendR = in.nextInt() - 1;
			int defendC = in.nextInt() - 1;
			score += turn(mat, attackR, attackC, direction, defendR, defendC, R, C);
		}

		System.out.println(score);
		print(before);
	}

	private static int turn(int mat[][], int attackR, int attackC, Direction direction, int defendR, int defendC,
			int maxRow, int maxCol) {
		int[][] after = clone(before, maxRow, maxCol);
		attack(mat, after, attackR, attackC, direction, maxRow, maxCol);
		int score = calculateScore(after, maxRow, maxCol);
		defend(after, defendR, defendC);

		before = after;
		return score;
	}

	private static void attack(int mat[][], int[][] after, int attackR, int attackC, Direction direction, int maxRow,
			int maxCol) {
		if (after[attackR][attackC] == FALL) {
			return;
		}

		after[attackR][attackC] = FALL;
		for (int i = 1; i < mat[attackR][attackC]; i++) {
			int newRow = attackR + direction.row * i;
			int newCol = attackC + direction.col * i;
			// System.out.println(newRow + " " + newCol + " " + direction.toString());
			if (isBoundary(newRow, newCol, maxRow, maxCol) && after[newRow][newCol] == STAND) {
				attack(mat, after, newRow, newCol, direction, maxRow, maxCol);
			}
		}
	}

	private static boolean isBoundary(int row, int col, int maxRow, int maxCol) {
		if (row < 0) {
			return false;
		}
		if (col < 0) {
			return false;
		}
		if (row >= maxRow) {
			return false;
		}
		if (col >= maxCol) {
			return false;
		}
		return true;
	}

	private static void defend(int[][] after, int defendR, int defendC) {
		after[defendR][defendC] = STAND;
	}

	private static int calculateScore(int after[][], int row, int col) {
		int sum = 0;

		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				if (before[i][j] == STAND && after[i][j] == FALL) {
					sum++;
				}
			}
		}
		return sum;
	}

	private static int[][] clone(int mat[][], int row, int col) {
		int newMat[][] = new int[row][col];

		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				newMat[i][j] = mat[i][j];
			}
		}

		return newMat;
	}

	private static void print(int mat[][]) {
		int row = mat.length;
		int col = mat[0].length;

		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				if (mat[i][j] == STAND) {
					System.out.print("S ");
				} else {
					System.out.print("F ");
				}
			}
			System.out.println();
		}
	}
}

enum Direction {
	N(-1, 0), W(0, -1), E(0, 1), S(1, 0);

	int row;
	int col;

	Direction(int row, int col) {
		this.row = row;
		this.col = col;
	}

	public String toString() {
		return this.name() + "(" + this.row + ", " + this.col + ")";
	}
}

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

boj 2631: 줄세우기  (0) 2021.04.01
boj 20166: 문자열 지옥에 빠진 호석  (1) 2021.03.26
boj 20164: 홀수 홀릭 호석  (0) 2021.03.24
boj 11663: 선분 위의 점  (0) 2021.03.17
boj 19637: IF문 좀 대신 써줘  (0) 2021.03.17

N의 범위는 10^9-1 까지이므로 8자리까지 들어올 수 있다.

가장 큰 수가 들어오는 경우를 생각해도 8자리를 3가지 뽑는 경우는 8C3 = 56가지이고,

재귀 함수의 깊이는 그렇게 깊지 않으므로 dp를 사용하지 않아도 될 것이라고 생각했다.

 

 

풀다가 한 가지 실수해서 테스트케이스에 걸린 부분이 있는데,

length 를 구할 때, 아무 생각 없이 Math.ceil로 구해버렸다가 테스트케이스 100 이 들어오면 length = 2가 되어버린다는 것을 깨달았다...

고친 풀이는 아래와 같다.

import java.util.*;

public class Main {

	private static int max = 0;
	private static int min = Integer.MAX_VALUE;

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

		int N = in.nextInt();
		findAll(N, calcOdds(N));
		System.out.println(min + " " + max);
	}

	private static void findAll(int num, int odd) {
		if (num < 10) {
			max = Math.max(max, odd);
			min = Math.min(min, odd);
			return;
		}

		if (num < 100) {
			int newNum = num / 10 + num % 10;
			int newOdd = calcOdds(newNum);
			findAll(newNum, odd + newOdd);
			return;
		}

		int length = (int) Math.floor(Math.log10(num)) + 1;
		for (int firstLength = 1; firstLength <= length - 2; firstLength++) {
			for (int secondLength = 1; secondLength <= length - firstLength - 1; secondLength++) {
				int thirdLength = length - firstLength - secondLength;

				int secondPow = (int) Math.pow(10, secondLength);
				int thirdPow = (int) Math.pow(10, thirdLength);

				int firstNum = num / secondPow / thirdPow;
				int secondNum = (num / thirdPow) % secondPow;
				int thirdNum = num % thirdPow;

				int newNum = firstNum + secondNum + thirdNum;
				int newOdd = calcOdds(newNum);
				findAll(newNum, odd + newOdd);
			}
		}
	}

	private static int calcOdds(int num) {
		int sum = 0;
		while (num != 0) {
			sum += (num % 10) % 2;
			num /= 10;
		}
		return sum;
	}
}

일단 아이디어 자체는 왼쪽에서 포함되는 점의 위치 leftIndex와 오른쪽에서 포함되는 점의 위치 rightIndex를 각각 구한 뒤,

rightIndex - leftIndex + 1 을 하여 구해 보자는 생각이었다.

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

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 points = Integer.parseInt(st.nextToken());
		int lines = Integer.parseInt(st.nextToken());

		int point[] = new int[points];

		st = new StringTokenizer(br.readLine());
		for (int i = 0; i < points; i++) {
			point[i] = Integer.parseInt(st.nextToken());
		}
		Arrays.sort(point);

		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
		for (int i = 0; i < lines; i++) {
			st = new StringTokenizer(br.readLine());
			int left = Integer.parseInt(st.nextToken());
			int right = Integer.parseInt(st.nextToken());

			int rightIndex = getRightIndex(right, points - 1, point);
			int leftIndex = getLeftIndex(left, points - 1, point);
			// System.out.println(leftIndex + " " + rightIndex);
			int sum = rightIndex - leftIndex + 1;
			sum = Math.max(0, sum);
			bw.write(sum + "\n");
		}
		bw.flush();
		br.close();
	}

	private static int getLeftIndex(int target, int length, int[] point) {
		int start = 0;
		int end = length;
		int answer = end;

		while (start <= end) {
			int mid = (start + end) / 2;

			if (target < point[mid]) {
				answer = Math.min(answer, mid);
				end = mid - 1;
			} else if (target == point[mid]) {
				return mid;
			} else {
				start = mid + 1;
			}
		}
		return answer;
	}

	private static int getRightIndex(int target, int length, int[] point) {
		int start = 0;
		int end = length;
		int answer = 0;

		while (start <= end) {
			int mid = (start + end) / 2;

			if (point[mid] < target) {
				answer = Math.max(answer, mid);
				start = mid + 1;
			} else if (point[mid] == target) {
				return mid;
			} else {
				end = mid - 1;
			}
		}
		return answer;
	}
}

 

하지만, 0이나 40과 같이 범위를 벗어나는 선분인 경우 따로 처리가 필요했다.

(예를 들어 선분 40 40 의 경우 기존 코드에서는 범위에 벗어난 선분이지만 점 1개를 리턴하고 있었다.)

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

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 points = Integer.parseInt(st.nextToken());
		int lines = Integer.parseInt(st.nextToken());

		int point[] = new int[points];

		st = new StringTokenizer(br.readLine());
		for (int i = 0; i < points; i++) {
			point[i] = Integer.parseInt(st.nextToken());
		}
		Arrays.sort(point);

		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
		for (int i = 0; i < lines; i++) {
			st = new StringTokenizer(br.readLine());
			int left = Integer.parseInt(st.nextToken());
			int right = Integer.parseInt(st.nextToken());

			int rightIndex = getRightIndex(right, points - 1, point);
			int leftIndex = getLeftIndex(left, points - 1, point);
			// System.out.println(leftIndex + " " + rightIndex);
			int sum = calcPoints(leftIndex, rightIndex, points);
			bw.write(sum + "\n");
		}
		bw.flush();
		br.close();
	}

	private static int calcPoints(int leftIndex, int rightIndex, int length) {
		if (rightIndex < 0 && leftIndex < 0) {
			return 0;
		}

		if (rightIndex >= length && leftIndex >= length) {
			return 0;
		}

		if (leftIndex < 0 && rightIndex >= length) {
			return length;
		}

		if (leftIndex < 0) {
			return rightIndex + 1;
		}

		if (rightIndex >= length) {
			return length - leftIndex;
		}

		return rightIndex - leftIndex + 1;
	}

	private static int getLeftIndex(int target, int length, int[] point) {
		int start = 0;
		int end = length;
		int answer = end;

		if (target < point[0]) {
			return -1;
		}
		if (point[end] < target) {
			return end + 1;
		}

		while (start <= end) {
			int mid = (start + end) / 2;

			if (target < point[mid]) {
				answer = Math.min(answer, mid);
				end = mid - 1;
			} else if (target == point[mid]) {
				return mid;
			} else {
				start = mid + 1;
			}
		}
		return answer;
	}

	private static int getRightIndex(int target, int length, int[] point) {
		int start = 0;
		int end = length;
		int answer = 0;

		if (target < point[0]) {
			return -1;
		}
		if (point[end] < target) {
			return end + 1;
		}

		while (start <= end) {
			int mid = (start + end) / 2;

			if (point[mid] < target) {
				answer = Math.max(answer, mid);
				start = mid + 1;
			} else if (point[mid] == target) {
				return mid;
			} else {
				end = mid - 1;
			}
		}

		return answer;
	}
}

이렇게 귀찮아질 바에는 반대로 생각하면 조금 더 쉬워진다. 지금은 포함되는 점의 시작점, 끝점을 구했지만

포함되지 않는 부분의 점들을 구하게 된다면 풀이는 조금 더 간단해 질 수 있음..

 

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

boj 20165: 인내의 도미노 장인 호석  (0) 2021.03.24
boj 20164: 홀수 홀릭 호석  (0) 2021.03.24
boj 19637: IF문 좀 대신 써줘  (0) 2021.03.17
boj 2805: 나무 자르기  (0) 2021.03.16
boj 2417: 정수 제곱근  (0) 2021.03.15

칭호는 전투력 상한값의 비내림차순으로 주어진다. = 오름차순 이란 말인가??

상당히 찝찝하지만... 오름차순으로 이해하고 문제를 풀었다.

 

이 문제에서 주의할 점은 입출력이 많기 때문에 내가 평소에 애용하는 Scanner/System.out.println을 써 버리면 시간초과가 난다는 점이다.

BufferedReader/BufferedWriter를 써버릇 해야 하는데.. 길고 귀찮아서 굳이 틀려야 바꿔보게 된다... 담엔 입출력 갯수부터 파악하자...ㅠ

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

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 n = Integer.parseInt(st.nextToken());
		int m = Integer.parseInt(st.nextToken());
		String name[] = new String[n];
		int power[] = new int[n];
		for (int i = 0; i < n; i++) {
			st = new StringTokenizer(br.readLine());
			name[i] = st.nextToken();
			power[i] = Integer.parseInt(st.nextToken());
		}

		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
		for (int i = 0; i < m; i++) {
			int characterPower = Integer.parseInt(br.readLine());

			bw.write(getName(characterPower, n, power, name) + "\n");
		}
		bw.flush();
		br.close();
	}

	private static String getName(int characterPower, int length, int[] power, String[] name) {
		int start = 0;
		int end = length - 1;
		int answer = end;
		while (start <= end) {
			int mid = (start + end) / 2;

			if (characterPower <= power[mid]) {
				answer = Math.min(mid, answer);
				end = mid - 1;
			} else {
				start = mid + 1;
			}
		}

		return name[answer];
	}
}

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

boj 20164: 홀수 홀릭 호석  (0) 2021.03.24
boj 11663: 선분 위의 점  (0) 2021.03.17
boj 2805: 나무 자르기  (0) 2021.03.16
boj 2417: 정수 제곱근  (0) 2021.03.15
boj 1789: 수들의 합  (1) 2021.03.15

상근이는 환경에 매우 관심이 많기 때문에, 나무를 필요한 만큼만 집으로 가져가려고 한다.

이때, 적어도 M미터의 나무를 집에 가져가기 위해서 절단기에 설정할 수 있는 높이의 최댓값을 구하는 프로그램을 작성하시오.

=> 최소의 최대를 구하는 문제는 뭐다?? = 이분탐색이다~~

 

import java.util.*;

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

		int n = in.nextInt();
		long length = in.nextLong();
		long tree[] = new long[n];

		long start = 0;
		long end = 0;
		for (int i = 0; i < n; i++) {
			tree[i] = in.nextLong();
			end = Math.max(end, tree[i]);
		}

		long answer = 0;
		while (start <= end) {
			long mid = (start + end) / 2;
			// System.out.println(start + " " + answer + ", " + mid + " " + end);
			if (sumTree(mid, tree) >= length) {
				answer = Math.max(mid, answer);
				start = mid + 1;
			} else {
				end = mid - 1;
			}
		}
		System.out.println(answer);
	}

	private static long sumTree(long cut, long[] trees) {
		long sum = 0;
		for (long tree : trees) {
			sum += tree >= cut ? tree - cut : 0;
		}

		return sum;
	}
}

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

boj 11663: 선분 위의 점  (0) 2021.03.17
boj 19637: IF문 좀 대신 써줘  (0) 2021.03.17
boj 2417: 정수 제곱근  (0) 2021.03.15
boj 1789: 수들의 합  (1) 2021.03.15
boj 1339: 단어 수학  (0) 2021.02.14

첨에 아무 생각 없이 mid*mid를 롱 타입으로 계산했는데 오버 플로우가 발생해서 BigInteger로 계산하도록 했다.

import java.math.BigInteger;
import java.util.*;

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

		long n = in.nextLong();
		long end = n;
		long start = 0;
		long answer = n;

		while (start <= end) {
			long mid = (start + end) / 2;
			BigInteger bigMid = BigInteger.valueOf(mid);

			if (bigMid.multiply(bigMid).compareTo(BigInteger.valueOf(n)) >= 0) {
				answer = Math.min(answer, mid);
				end = mid - 1;
			} else {
				start = mid + 1;
			}
		}

		System.out.println(answer);
	}
}

 

 

  • java에서 자료형
    • Long - 64비트(-9223372036854775808 ~ 9223372036854775807)
    • BigInteger - 무한한 값

 

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

boj 19637: IF문 좀 대신 써줘  (0) 2021.03.17
boj 2805: 나무 자르기  (0) 2021.03.16
boj 1789: 수들의 합  (1) 2021.03.15
boj 1339: 단어 수학  (0) 2021.02.14
boj 2638: 치즈  (0) 2021.02.14

+ Recent posts