import java.util.*;

public class Main {
	public static int henry(int a, int b) {
		int n = b / a;
		if (n * a >= b) {
			return n;
		}
		return n + 1;
	}

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

		for (int i = 0; i < testcase; i++) {
			int a = in.nextInt();
			int b = in.nextInt();

			while (true) {
				int x = henry(a, b);
				
				if (a * x == b) {
					System.out.println(x);
					break;
				}
				
				a = a * x - b;
				b = b * x;
			}
		}
	}
}

 

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

boj 15686: 치킨배달  (0) 2020.12.30
boj 1915: 가장 큰 정사각형  (0) 2020.12.24
boj 1520: 내리막길  (0) 2020.12.15
boj 11066: 파일 합치기  (0) 2020.07.21
boj 1509: 팰린드롬 분할  (0) 2020.07.21

DP 문제로 보고 풀었는데 다른 분들이 실행 시간이 현저히 적어서 비교해 봐야겠음...

import java.util.Scanner;

public class Solution {
	public static int mat[][], dp[][];

	public static void main(String[] args) throws Exception {
		Scanner in = new Scanner(System.in);
		int row = in.nextInt();
		int col = in.nextInt();
		mat = new int[row][col];
		dp = new int[row][col];
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				mat[i][j] = in.nextInt();
				dp[i][j] = -1;
			}
		}

		System.out.println(getRouteNums(row - 1, col - 1, row, col));
	}

	public static int getRouteNums(int row, int col, int maxRow, int maxCol) {
		if (row == 0 && col == 0) {
			return 1;
		}
		if (dp[row][col] != -1) {
			return dp[row][col];
		}

		int left = row - 1 >= 0 && mat[row][col] < mat[row - 1][col] ? getRouteNums(row - 1, col, maxRow, maxCol) : 0;
		int right = row + 1 < maxRow && mat[row][col] < mat[row + 1][col] ? getRouteNums(row + 1, col, maxRow, maxCol): 0;
		int up = col - 1 >= 0 && mat[row][col] < mat[row][col - 1] ? getRouteNums(row, col - 1, maxRow, maxCol) : 0;
		int down = col + 1 < maxCol && mat[row][col] < mat[row][col + 1] ? getRouteNums(row, col + 1, maxRow, maxCol): 0;
		return dp[row][col] = left + right + up + down;
	}
}

 

1. bufferedReader 대신 Scanner를 쓴 점

2. getRouteNums의 4방향 값을 구할 때, dp에 있는 값이더라도 굳이 재귀함수 호출을 하게 되어 시간이 더 걸린 것 같다.

 

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

boj 1915: 가장 큰 정사각형  (0) 2020.12.24
boj 10253: 헨리  (0) 2020.12.24
boj 11066: 파일 합치기  (0) 2020.07.21
boj 1509: 팰린드롬 분할  (0) 2020.07.21
boj 10942: 팰린드롬?  (0) 2020.07.20

"연속된" 파일을 합쳐서 하나의 파일로 만들 때 비용이 최소일 때를 구하는 문제이다.

 

DP[i][j] = i ~ j번 파일을 합치는 최소 비용이라고 할 때,

DP[i][j] = min(DP[i][k] + DP[k+1][j]) + (A[i] ~ A[j] 까지의 합) 일 것이고 시간 복잡도는 O(N^3)이다.

 

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

public class Main {
	public static void main(String[] args) throws NumberFormatException, IOException {
		Scanner in = new Scanner(System.in);
		int test = in.nextInt();
		for (int t = 0; t < test; t++) {
			int n = in.nextInt();
			int page[] = new int[n + 1];
			int pageSum[] = new int[n + 1];
			for (int i = 1; i <= n; i++) {
				page[i] = in.nextInt();
				pageSum[i] = pageSum[i - 1] + page[i];
			}
			int dp[][] = new int[n + 1][n + 1];
			for (int i = 1; i <= n; i++) {
				Arrays.fill(dp[i], -1);
			}
			System.out.println(DP(dp, pageSum, 1, n));
		}
	}

	public static int DP(int dp[][], int pageSum[], int i, int j) {
		if (i == j) {
			dp[i][j] = 0;
			return dp[i][j];
		}

		if (dp[i][j] != -1) {
			return dp[i][j];
		}

		for (int k = i; k < j; k++) {
			int temp = DP(dp, pageSum, i, k) + DP(dp, pageSum, k + 1, j) + pageSum[j] - pageSum[i - 1];
			if (dp[i][j] == -1 || dp[i][j] > temp) {
				dp[i][j] = temp;
			}
		}
		return dp[i][j];
	}
}

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

boj 10253: 헨리  (0) 2020.12.24
boj 1520: 내리막길  (0) 2020.12.15
boj 1509: 팰린드롬 분할  (0) 2020.07.21
boj 10942: 팰린드롬?  (0) 2020.07.20
boj 1890: 점프  (0) 2020.07.20

어떤 문자열을 팰린드롬으로 분할하는데, 분할 개수의 최솟값을 구하는 문제이다.

 

일단 팰린드롬의 연속이 되어야 하고,

정답의 최댓값은 N이 될 것이다. (길이가 1인 문자열은 항상 팰린드롬이니까)

 

DP[i]: str[i]까지 문자열까지의 팰린드롬 분할 갯수의 최솟값

DP[i] = Math.min(D[j-1]) + 1(단, j ~ i까지는 팰린드롬)

=> 시간복잡도는 DP를 채워야 하는 갯수 N * j를 찾는 시간 N * i~ j가 팰린드롬인지 아닌지 찾을 때 O(1): O(N^2)

 

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

public class Main {
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
		StringTokenizer st;

		String str = br.readLine();
		char[] arr = str.toCharArray();

		int n = str.length();
		int dp[][] = calcPalindrome(arr, n);
		int ans = calcDividedPalindrome(dp, n);
		bw.write(String.valueOf(ans));
		bw.flush();
		bw.close();
	}

	public static int[][] calcPalindrome(char arr[], int n) {
		int dp[][] = new int[n + 1][n + 1];

		// 길이가 1일 때
		for (int i = 1; i <= n; i++) {
			dp[i][i] = 1;
		}

		// 길이가 2일 때
		for (int i = 1; i <= n - 1; i++) {
			if (arr[i - 1] == arr[i]) {
				dp[i][i + 1] = 1;
			}
		}

		// 길이가 3 이상
		for (int i = 3; i <= n; i++) {
			for (int j = 1; j <= n - i + 1; j++) {
				int k = j + i - 1;
				if (arr[j - 1] == arr[k - 1] && dp[j + 1][k - 1] == 1) {
					dp[j][k] = 1;
				}
			}
		}

		return dp;
	}

	public static int calcDividedPalindrome(int dp[][], int n) {
		int maxDivided[] = new int[n + 1];

		for (int i = 1; i <= n; i++) {
			maxDivided[i] = -1;
			for (int j = 1; j <= i; j++) {
				if (dp[j][i] == 1) {
					if (maxDivided[i] == -1 || maxDivided[i] > maxDivided[j - 1] + 1) {
						maxDivided[i] = maxDivided[j - 1] + 1;
					}
				}
			}
		}

		return maxDivided[n];
	}
}

 

BufferedWriter is a method that flush the buffer and write the characters directly to the underlying stream.

따라서 int형의 ans를 바로 bw.write하게 되면 char형으로 output이 나온다.

따라서 String형으로 바꿔준 뒤 쓰도록 한다.

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

boj 1520: 내리막길  (0) 2020.12.15
boj 11066: 파일 합치기  (0) 2020.07.21
boj 10942: 팰린드롬?  (0) 2020.07.20
boj 1890: 점프  (0) 2020.07.20
boj 11660: 구간 합 구하기 5  (0) 2020.07.19

어떤 문자열이 팰린드롬인지 확인하는 2가지 방법 --> O(N)

  • s == reverse(s) 인지 확인
  • 모든 ch[i] == ch[s.length-i] 인지 확인

이 때, 쿼리의 갯수가 M개이므로 총 걸리는 시간은 O(MN)인데 시간 제한에 걸린다.

 

팰린드롬을 검사하는 다른 방법

  1. 길이가 1인 부분 수열은 반드시 팰린드롬 --> O(N)
  2. 길이가 2인 부분 수열은 두 수가 같을 때만 팰린드롬 --> O(N)
  3. 길이가 3이상인 경우는 DP를 이용한다.-->O(N^2)
    1. DP[i][j]: i ~ j까지 팰린드롬인지 아닌지 저장
    2. ch[i] == ch[j]이면 DP[i][j] = DP[i+1][j-1]이다.

=> O(N^2+M)만에 구할 수 있다.

 

public class Main {
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
		StringTokenizer st;

		int n = Integer.parseInt(br.readLine());
		int arr[] = new int[n + 1];
		st = new StringTokenizer(br.readLine());
		for (int i = 1; i <= n; i++) {
			arr[i] = Integer.parseInt(st.nextToken());
		}
		int dp[][] = new int[n + 1][n + 1];

		dp = calcPalindrome(arr, n);

		int m = Integer.parseInt(br.readLine());
		for (int i = 0; i < m; i++) {
			st = new StringTokenizer(br.readLine());
			int start = Integer.parseInt(st.nextToken());
			int end = Integer.parseInt(st.nextToken());
			bw.write(dp[start][end] + "\n");
		}
		bw.flush();
		bw.close();
	}

	public static int[][] calcPalindrome(int arr[], int n) {
		int dp[][] = new int[n + 1][n + 1];

		// 길이가 1일 때
		for (int i = 1; i <= n; i++) {
			dp[i][i] = 1;
		}

		// 길이가 2일 때
		for (int i = 1; i <= n - 1; i++) {
			if (arr[i] == arr[i + 1]) {
				dp[i][i + 1] = 1;
			}
		}

		// 길이가 3 이상
		for (int i = 3; i <= n; i++) {
			for (int j = 1; j <= n - i + 1; j++) {
				int k = j + i - 1;
				if (arr[j] == arr[k] && dp[j + 1][k - 1] == 1) {
					dp[j][k] = 1;
				}
			}
		}

		return dp;
	}
}

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

boj 11066: 파일 합치기  (0) 2020.07.21
boj 1509: 팰린드롬 분할  (0) 2020.07.21
boj 1890: 점프  (0) 2020.07.20
boj 11660: 구간 합 구하기 5  (0) 2020.07.19
boj 1197: 최소 스패닝 트리  (0) 2020.07.19

(1, 1) -> (n, n)으로 가는 경로의 개수를 구하는 문제이다.

DP[i][j] = (i, j) 칸에 갈 수 있는 경로의 개수라고 하면 DP[1][1] = 1로 초기화한다.

 

(i, j)는 오른쪽 또는 아래로 이동하므로 (i+arr[i][j], j) 또는 (i, j+arr[i][j])로 이동한다.

 

총 시간 복잡도는 N^2 칸 * O(1) 이므로 O(N^2)이다.

 

import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int arr[][] = new int[n + 1][n + 1];
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				arr[i][j] = in.nextInt();
			}
		}

		long dp[][] = new long[n + 1][n + 1];
		dp[1][1] = 1;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {

				if (arr[i][j] == 0) {
					continue;
				}

				if (i + arr[i][j] <= n) {
					dp[i + arr[i][j]][j] += dp[i][j];
				}
				if (j + arr[i][j] <= n) {
					dp[i][j + arr[i][j]] += dp[i][j];
				}

			}
		}
		System.out.println(dp[n][n]);
	}
}

 

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

boj 1509: 팰린드롬 분할  (0) 2020.07.21
boj 10942: 팰린드롬?  (0) 2020.07.20
boj 11660: 구간 합 구하기 5  (0) 2020.07.19
boj 1197: 최소 스패닝 트리  (0) 2020.07.19
boj 2357: 최솟값과 최댓값  (0) 2020.07.18

이 문제는 2차원에서의 누적 합을 구하는 문제이다.

 

sum[i][j]: (1, 1) ~ (i, j)까지의 합이라고 할 때,

sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + arr[i][j]와 같다

 

마지막으로 (x1, y1) ~ (x2, y2)의 값은 s[x2][y2] - s[x2][y-1] - s[x1-1][y2] + s[x1-1][y1-1]이다.

 

import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int m = in.nextInt();
		int arr[][] = new int[n + 1][n + 1];
		int sum[][] = new int[n + 1][n + 1];
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				arr[i][j] = in.nextInt();
				sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + arr[i][j];
			}
		}

		for (int i = 0; i < m; i++) {
			int x1 = in.nextInt();
			int y1 = in.nextInt();
			int x2 = in.nextInt();
			int y2 = in.nextInt();

			int ans = sum[x2][y2] - sum[x2][y1 - 1] - sum[x1 - 1][y2] + sum[x1 - 1][y1 - 1];
			System.out.println(ans);
		}
	}
}

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

boj 10942: 팰린드롬?  (0) 2020.07.20
boj 1890: 점프  (0) 2020.07.20
boj 1197: 최소 스패닝 트리  (0) 2020.07.19
boj 2357: 최솟값과 최댓값  (0) 2020.07.18
boj 10986: 나머지 합  (0) 2020.07.18

스패닝 트리: 그래프에서 일부 간선을 선택해서 만든 트리

최소 스패닝 트리: 스패닝 트리 중에 선택한 간선의 가중치 합이 최소인 트리

 

스패닝 트리를 구하는 알고리즘

  • Prim: 정점을 점점 확장해 나가는 방식 (힙을 써서 구현 - 정점번호, 가중치 저장) --> 최소값을 우선 순위 큐를 이용하면 최소 값을 logE만에 찾을 수 있다. 이 경우 시간복잡도는 O(ElogE)가 된다. 모든 간선이 힙에 한번 씩 들어갔다가 나오기 때문
    1. 그래프에서 아무 정점이나 선택한다.
    2. 선택한 정점과 선택하지 않은 정점을 연결하는 간선 중에 최소값을 고른다. 이 간선을 (u, v)라고 한다.
    3. 선택한 간선을 최소 스패닝 트리에 추가하고, 선택하지 않았던 정점 v를 선택한다.
    4. 2번으로 돌아간다
  • Kruskal: 간선을 점점 확장해 나가는 방법
    1. 가중치가 작은 edge부터 살펴본다 --> 간선의 길이 순으로 먼저 정렬해야 한다. O(ElogE)
    2. edge e가 (u, v, c)일 때,  u와 v가 서로 다른 집합이면 e를 최소 스패닝 트리에 추가한다. O(E) - union/find 연산

 

Prim 알고리즘 풀이

import java.util.*;

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

		LinkedList<Edge>[] pair = new LinkedList[v + 1];
		HashSet<Integer> vSet = new HashSet<Integer>();
		for (int i = 1; i <= v; i++) {
			pair[i] = new LinkedList<Edge>();
			vSet.add(i);
		}
		for (int i = 0; i < e; i++) {
			int start = in.nextInt();
			int end = in.nextInt();
			long cost = in.nextLong();

			pair[start].add(new Edge(end, cost));
			pair[end].add(new Edge(start, cost));
		}

		int currentNode = 1;
		long ans = 0;
		vSet.remove(1);

		PriorityQueue<Edge> pq = new PriorityQueue<Edge>();
		pq.addAll(pair[currentNode]);

		while (!vSet.isEmpty()) {
			Edge minEdge = pq.poll();

			if (vSet.contains(minEdge.v)) {
				ans += minEdge.cost;
				currentNode = minEdge.v;
				vSet.remove(currentNode);
				pq.addAll(pair[currentNode]);
			}
		}
		System.out.println(ans);
	}
}

class Edge implements Comparable<Edge> {
	int v;
	long cost;

	public Edge(int v, long cost) {
		this.v = v;
		this.cost = cost;
	}

	@Override
	public int compareTo(Edge o) {
		return Long.compare(this.cost, o.cost);
	}
}

 

Kruskal 알고리즘 풀이

import java.util.*;

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

		PriorityQueue<Edge> pq = new PriorityQueue<Edge>();
		int vSet[] = new int[v + 1];

		for (int i = 1; i <= v; i++) {
			vSet[i] = i;
		}

		for (int i = 0; i < e; i++) {
			int start = in.nextInt();
			int end = in.nextInt();
			long cost = in.nextLong();

			pq.add(new Edge(start, end, cost));
		}

		long ans = 0;
		if (!pq.isEmpty()) {
			Edge firstEdge = pq.poll();
			ans += firstEdge.cost;
			union(firstEdge.v1, firstEdge.v2, vSet);
		}

		while (!isFinished(vSet) && !pq.isEmpty()) {
			Edge minEdge = pq.poll();

			int v1Contains = find(minEdge.v1, vSet);
			int v2Contains = find(minEdge.v2, vSet);

			if (v1Contains != v2Contains) {
				ans += minEdge.cost;
				union(minEdge.v1, minEdge.v2, vSet);
			}
		}
		System.out.println(ans);
	}

	public static void union(int x, int y, int[] vSet) {
		int ux = find(x, vSet);
		int uy = find(y, vSet);

		vSet[uy] = ux;
	}

	public static int find(int x, int[] vSet) {
		if (vSet[x] == x) {
			return x;
		}

		vSet[x] = find(vSet[x], vSet);
		return vSet[x];
	}

	public static boolean isFinished(int[] vSet) {
		int now = vSet[1];
		for (int i = 1; i < vSet.length; i++) {
			if (vSet[i] != now) {
				return false;
			}
		}
		return true;
	}
}

class Edge implements Comparable<Edge> {
	int v1, v2;
	long cost;

	public Edge(int v1, int v2, long cost) {
		this.v1 = v1;
		this.v2 = v2;
		this.cost = cost;
	}

	@Override
	public int compareTo(Edge o) {
		return Long.compare(this.cost, o.cost);
	}
}

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

boj 1890: 점프  (0) 2020.07.20
boj 11660: 구간 합 구하기 5  (0) 2020.07.19
boj 2357: 최솟값과 최댓값  (0) 2020.07.18
boj 10986: 나머지 합  (0) 2020.07.18
boj 11003: 최솟값 찾기  (0) 2020.07.18

+ Recent posts