N x N 크기의 정사각형 격자 형태에서 (0, 0) ~ (N-1, N-1)까지 경주로를 만드는데

직선 도로 하나를 만들 때는 100원이 소요되며, 코너를 하나 만들 때는 500원이 든다.

경주로를 만드는 데 최소 비용을 구하는 문제이다.

 

1차 시도 - BFS로 모든 방향에 대한 비용을 구하면서 재방문의 경우는 제외해주도록 한다. -> O(3^N)이 걸릴 것이기 때문에 시간 초과..

심지어 정확도도 틀림

import java.util.*;
class Solution {
	public static int dr[] = { 0, 1, 0, -1 };
	public static int dc[] = { 1, 0, -1, 0 };

	public int solution(int[][] board) {
		Queue<Move> queue = new LinkedList<Move>();
		for (int i = 0; i < 4; i++) {
			queue.add(new Move(dr[i], dc[i], i, 100)); // 이 부분이 벽일 수도 있기 때문에 정확도가 틀린 것이었다.
		}

		int min = Integer.MAX_VALUE;
		int length = board.length;

		while (!queue.isEmpty()) {
			Move now = queue.poll();

			if (now.row == length - 1 && now.col == length - 1) {
				min = Math.min(min, now.value);
			}

			for (int i = 0; i < 4; i++) {
				if (Math.abs(i - now.direction) == 2) {
					continue;
				}

				int newRow = now.row + dr[i];
				int newCol = now.col + dc[i];
				int newValue = now.direction == i ? 100 : 600;
				newValue += now.value;

				if (isBoundary(newRow, newCol, length) && newValue > 0 && newValue < min
						&& board[newRow][newCol] == 0) {
					queue.add(new Move(newRow, newCol, i, newValue));
				}
			}

		}

		return min;
	}

	public boolean isBoundary(int row, int col, int N) {
		if (row < 0) {
			return false;
		}
		if (col < 0) {
			return false;
		}
		if (row >= N) {
			return false;
		}
		if (col >= N) {
			return false;
		}
		return true;
	}
}

class Move {
	int row;
	int col;
	int direction;
	int value;

	public Move(int row, int col, int direction, int value) {
		this.row = row;
		this.col = col;
		this.direction = direction;
		this.value = value;
	}
}

채점 결과

정확성: 30.4

합계: 30.4 / 100.0

 

 

 

2차시도 - 재 방문을 줄이자 => DP를 사용해볼까? 그런데 방향마다 더해지는 값이 달라서 DP에도 다르게 저장해야함

DP[i][j][k]: (0, 0) ~ (i, j)까지 k방향으로 움직였을 때 최소비용이라고 정의하도록 하자

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

class Solution {
	public static int dr[] = { 0, 1, 0, -1 };
	public static int dc[] = { 1, 0, -1, 0 };

	public int solution(int[][] board) {

		int min = Integer.MAX_VALUE;
		int length = board.length;

		int dp[][][] = new int[length][length][4];

		Queue<Move> queue = new LinkedList<Move>();
		for (int i = 0; i < 4; i++) {
			queue.add(new Move(0, 0, i, 0));
		}

		while (!queue.isEmpty()) {
			Move now = queue.poll();

			if (now.row == length - 1 && now.col == length - 1) {
				min = Math.min(min, dp[now.row][now.col][now.direction]);
			}

			for (int i = 0; i < 4; i++) {
				if (Math.abs(i - now.direction) != 2) { // 후진 제외
					int newRow = now.row + dr[i];
					int newCol = now.col + dc[i];
					int newValue = (now.direction == i ? 100 : 600) + now.value;

					if (isBoundary(newRow, newCol, length) && board[newRow][newCol] == 0) {
						if (dp[newRow][newCol][i] >= newValue || dp[newRow][newCol][i] == 0) {
							dp[newRow][newCol][i] = newValue;
							queue.add(new Move(newRow, newCol, i, newValue));
						}
					}
				}
			}
		}

		return min;
	}

	public boolean isBoundary(int row, int col, int N) {
		if (row < 0) {
			return false;
		}
		if (col < 0) {
			return false;
		}
		if (row >= N) {
			return false;
		}
		if (col >= N) {
			return false;
		}
		return true;
	}
}

class Move {
	int row;
	int col;
	int direction;
	int value;

	public Move(int row, int col, int direction, int value) {
		this.row = row;
		this.col = col;
		this.direction = direction;
		this.value = value;
	}
}

 

 

 

 

 

최단경로를 구하는 것이므로 길을 가는 방법은 오른쪽 또는 아래로 이동하는 방법밖에 없다.

즉, 현재 위치의 가짓수는 현재 위치의 위 또는 왼쪽에서 가능한 가짓수를 더한 값이다. => DP

 

이를 식으로 표현해 보면 DP[i][j] = DP[i-1][j] + DP[i][j-1]이다.

import java.util.*;
class Solution {
	public long solution(int m, int n, int[][] puddles) {
		long mat[][] = new long[n + 1][m + 1];
		for (int i = 1; i < mat.length; i++) {
			Arrays.fill(mat[i], -100);
			mat[i][0] = 0;
		}

		for (int i = 0; i < puddles.length; i++) {
			mat[puddles[i][1]][puddles[i][0]] = -1;
		}

		mat[1][1] = 1;

		findAll(n, m, mat);
		//printMat(mat);

		return mat[n][m];
	}

	public long findAll(int row, int col, long[][] mat) {
		if (mat[row][col] != -100) {
			return mat[row][col];
		}
		long left = findAll(row, col - 1, mat);
		long upper = findAll(row - 1, col, mat);

		if (left == -1 && upper == -1) {
			mat[row][col] = 0;
			return mat[row][col];
		}
		if (left == -1) {
			mat[row][col] = upper;
			return mat[row][col];
		}
		if (upper == -1) {
			mat[row][col] = left;
			return mat[row][col];
		}

		mat[row][col] = (left + upper) % 1000000007;
		return mat[row][col];
	}

	public static void printMat(long[][] mat) {
		for (int i = 0; i < mat.length; i++) {
			for (int j = 0; j < mat[i].length; j++) {
				System.out.print(mat[i][j] + " ");
			}
			System.out.println();
		}
	}
}

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

Programmers 67259: 경주로 건설  (0) 2021.01.05
Programmers 12938: 최고의 집합  (0) 2021.01.05
Programmers 17685: 자동완성  (0) 2020.12.30
Programmers 43236: 징검다리  (0) 2020.12.26
Programmers 12907: 거스름돈  (0) 2020.12.24

어차피 모든 경우의 수를 구하긴 해야한다.

하지만, 팰린드롬임을 한번 체크했다면 계속 그 글자가 팰린드롬인지 체크를 하지 않도록 어딘가 저장하는 방법은 없을지? => DP

 

팰린드롬 s의 서브스트링 s.substring(1, n-1) 또한 팰린드롬임을 이용하자.

=> DP[indexStart][indexEnd]: s.substring(indexStart, indexEnd)의 팰린드롬 여부

 

맨 첫 글자와 끝 글자가 같은 경우 중에서 팰린드롬이 될 수 있는 경우

1. 맨첫글자 위치 == 끝글자 위치 ex) "a"

2. 두 글자가 연속인 경우 ex) "aa"

3. 중간에 한 글자가 껴있는 경우 ex) "aba"

4. s.substring(첫 글자의 다음 글자, 끝 글자의 이전 글자)가 팰린드롬인 경우

 

 

import java.util.*;
class Solution {
	public List<List<String>> partition(String s) {
		List<List<String>> answer = new ArrayList<List<String>>();

		int length = s.length();
		boolean[][] dp = new boolean[length][length];

		palindrome(s, 0, answer, new Stack<String>(), dp);

		return answer;
	}

	public void palindrome(String s, int indexStart, List<List<String>> answer, Stack<String> currentList, boolean[][] dp) {
		if (indexStart >= s.length()) {
			answer.add(new ArrayList<>(currentList));
			return;
		}

		for (int end = indexStart; end < s.length(); end++) {
			if (s.charAt(indexStart) == s.charAt(end)) {
				if (end - 1 <= indexStart + 1 || dp[indexStart + 1][end - 1]) {
					dp[indexStart][end] = true;
					currentList.add(s.substring(indexStart, end + 1));
					palindrome(s, end + 1, answer, currentList, dp);
					currentList.pop();
				}
			}
		}
	}
}

 

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

leetcode: Trapping Rain Water  (0) 2020.12.27
leetcode: Median of two sorted Arrays  (0) 2020.12.26
leetcode: Letter Combinations of a Phone number  (0) 2020.12.26
leetcode: Contain With Most Water  (0) 2020.12.17
leetcode: 4sum  (0) 2020.12.17

1 ≤ n, m ≤ 1,000 이므로 브루트포스로 다 구해본다면 O(N^2M^2)로 시간 제한에 걸릴 것이다.

array를 무조건 한 번은 봐야하기 때문에 O(NM) * ???가 2초 안에 실행 되어야 한다.

 

DP로 풀어볼 수 있는 방법을 생각해 보자

dp[i][j]: (i,j)를 오른쪽 아래로 포함한 가장 크게 만들 수 있는 정사각형의 한 변 길이로 잡고, mat[i][j]==1일때 dp를 업데이트 한다.

import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int row = in.nextInt();
		int col = in.nextInt();
		int mat[][] = new int[row][col];
		int dp[][] = new int[row][col];
		int maxLength = 0;

		for (int i = 0; i < row; i++) {
			String rowInput = in.next();
			int j = 0;
			for (char ch : rowInput.toCharArray()) {
				mat[i][j] = ch - '0';
				dp[i][j] = ch - '0';
				maxLength = ch - '0' > 0 ? 1 : 0;
				j++;
			}
		}

		for (int i = 1; i < row; i++) {
			for (int j = 1; j < col; j++) {
				if (mat[i][j] != 0) {
					dp[i][j] = findMinValueBetweenThree(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1;
					maxLength = Math.max(maxLength, dp[i][j]);
				}
			}
		}
		System.out.println(maxLength * maxLength);
	}

	public static int findMinValueBetweenThree(int a, int b, int c) {
		return Math.min(a, Math.min(b, c));
	}
}

 

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

boj 1655: 가운데를 말해요  (0) 2021.01.03
boj 15686: 치킨배달  (0) 2020.12.30
boj 10253: 헨리  (0) 2020.12.24
boj 1520: 내리막길  (0) 2020.12.15
boj 11066: 파일 합치기  (0) 2020.07.21

dp[n]: n원을 낼 수 있는 가짓수로 정의한다.

class Solution {
	public int solution(int n, int[] money) {
		int dp[] = new int[n + 1];
		dp[0] = 1;

		for (int coin : money) {
			for (int i = 1; i <= n; i++) {
				if (i - coin >= 0) {
					dp[i] += dp[i - coin];
				}
			}
		}

		return dp[n];
	}
}

 

 

왜 2중for문의 안과 밖이 바뀌면 중복된 값, 예를 들어 [10, 20, 10]과 [10, 10, 20]을 다른 경우로 체크해서 더 많은 값이 나온다.

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

어떤 문자열이 팰린드롬인지 확인하는 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

+ Recent posts