Skip to content
64 changes: 64 additions & 0 deletions BOJ/1000-5000번/JW_2098.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import java.util.Arrays;

public class JW_2098 {
static int n;
static int[][] distance; // 각 거리를 정보
static int[][] dp; // 메모이제이션을 위한 DP배열
static int INF = Integer.MAX_VALUE >> 2; // 적절한 최댓값

public static void main(String[] args) throws Exception {
n = read();
distance = new int[n][n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++) {
int d = read();
// 거리 정보가 없는 경우에는 최댓값으로 설정
if (d == 0)
d = INF;
distance[i][j] = d;
}
dp = new int[n][1 << n];
// dp배열 초기화
for (int i = 0; i < n; i++)
Arrays.fill(dp[i], -1);
// 0번 도시에서 출발하는 외판원 순회
int result = tsp(0, 1);
System.out.println(result);
}

// 외판원 순회
// @param city 현재 도시
// @param visited 비트마스킹 방문처리
private static int tsp(int city, int visited) {
// 모든 도시를 방문했다면
if (visited == (1 << n) - 1)
// 원래 도시로 돌아가는 거리를 반환
return distance[city][0];
// 미리 계산된 최솟값이 있다면 반환
if (dp[city][visited] != -1)
return dp[city][visited];
int result = INF; // 해당 도시를 방문했을 때 최솟값을 구하기 위한 변수
for (int nextCity = 0; nextCity < n; nextCity++) {
// 방문한 도시라면 건너뜀
if ((visited & (1 << nextCity)) != 0)
continue;
// 다음 도시를 방문
int temp = distance[city][nextCity] + tsp(nextCity, visited | (1 << nextCity));
// 해당 도시에서 가질 수 있는 최솟값 갱신
result = Math.min(result, temp);
}
// 계산된 값으로 메모이제이션
dp[city][visited] = result;
return result;
}

// 빠른 입력 함수
private static int read() throws Exception {
int c, n = System.in.read() & 15;
while ((c = System.in.read()) >= 48)
n = (n << 3) + (n << 1) + (c & 15);
if (c == 13)
System.in.read();
return n;
}
}
61 changes: 61 additions & 0 deletions BOJ/1000-5000번/JW_2660.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.StringTokenizer;

public class JW_2660 {

static final int INF = Integer.MAX_VALUE >> 2;

public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
// 그래프 선언 및 초기화
int[][] graph = new int[n + 1][n + 1];
for (int i = 0; i < n + 1; i++) {
Arrays.fill(graph[i], INF);
graph[i][i] = 0;
}
while (true) {
StringTokenizer st = new StringTokenizer(br.readLine());
int u = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
// 종료 조건
if (u == -1 && v == -1)
break;
// 양방향 그래프
graph[u][v] = 1;
graph[v][u] = 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

각 노드의 거리를 1이라고 설정하면 거리와 깊이의 개념이 같아지기 때문에 플로이드 와셜로 풀이할 수 있네여!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

후후 맞습니다! 담엔 플로이드 와샬로만 풀리는 문제를 내야겠어요...

}
// 플로이드 와샬
for (int k = 1; k < n + 1; k++)
for (int i = 1; i < n + 1; i++)
for (int j = 1; j < n + 1; j++)
if (graph[i][j] > graph[i][k] + graph[k][j]) {
graph[i][j] = graph[i][k] + graph[k][j];
}
int min = 50; // 최소 스코어
ArrayList<Integer> al = new ArrayList<>();
for (int i = 1; i < n + 1; i++) {
int score = 0;
// 최단 거리 중 최댓값 찾기
for (int j = 1; j < n + 1; j++)
score = Math.max(score, graph[i][j]);
// 최솟값을 갱신 할 수 있다면
if (min > score) {
min = score; // 최솟값 갱신
al.clear(); // 리스트 비우기
al.add(i); // 리스트에 추가
// 최솟값과 동일하면
} else if (min == score)
al.add(i); // 리스트에 추가
}
StringBuilder sb = new StringBuilder();
sb.append(max).append(" ").append(al.size()).append("\n");
for (int i : al) {
sb.append(i).append(" ");
}
System.out.println(sb);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제완님! 플로이드로 푸셨길래 문제에서 이해 안되는 부분 질문 드립니다
문제에서 구하는게 회장의 점수와 회장이 될 수 있는 모든 사람이잖아요
그래서 모든 회원 간의 점수를 구해서 점수가 가장 적은 사람(회장)을 구해야하는데 예시가.. 이해 안됩니다ㅠㅠ

"예를 들어 어느 회원이 다른 모든 회원과 친구이면, 이 회원의 점수는 1점이다. 어느 회원의 점수가 2점이면, 다른 모든 회원이 친구이거나 친구의 친구임을 말한다. 또한 어느 회원의 점수가 3점이면, 다른 모든 회원이 친구이거나, 친구의 친구이거나, 친구의 친구의 친구임을 말한다."

점수 1점 : 회원A-회원B 친구
점수 2점 : 다른 모든 회원이 친구(1점) or 친구의 친구
점수 3점 : 다른 모든 회원이 친구(1점) or 친구의 친구(2점) or 친구의 친구의 친구

"각 회원의 점수를 정할 때 주의할 점은 어떤 두 회원이 친구사이이면서 동시에 친구의 친구사이이면, 이 두사람은 친구사이라고 본다. " -> 최단 거리를 고려(??)

모든 사람 최소 점수 구하기 -> 플로이드???
예시가 이해가 잘안되어서 그런지 왜 최단 거리를 구해야되는지 잘모르겠습니다ㅠ_ㅠ

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혜원님! 점수를 누적하는게 아니라 한 회원의 모든 친구 정보를 파악해서 최종 점수를 매기는 것 입니다!

  • 점수 1점: 한 회원(A)가 모든 회원과 친구로 연결됨(거리: 1)
  • 점수 2점: 한 회원(A)가 B와 친구의 친구로 연결되고(거리: 2), 나머지 회원과는 친구로 연결됨(거리: 1)

이런식으로 점수를 계산하기 때문에 한 회원이 다른 회원으로 가는 최단거리를 구해서 그 값중 가장 먼 값이 점수가 됩니다

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일단 문제에서 플로이드 와샬을 적용하는 이유를 알려드릴게요!

  1. 회원 사이에 서로 모르는 사람도 있지만, 몇 사람을 통하면 모두가 서로 알 수 있다.
  2. 각 회원은 다른 회원들과 가까운 정도에 따라 점수를 받게 된다.
  3. 어느 회원이 다른 모든 회원과 친구이면, 이 회원의 점수는 1점

일단 1번 조건으로 인해서 모든 회원들은 이어질 수 있다는 점을 파악해야 합니다.
2번 조건에 의해 각 회원은 다른 회원들과 가까운 정도(회원과 회원의 최단 거리)에 따라서 점수를 얻기 때문에 회원 간의 최단 거리를 구해야 합니다.
3번 조건에 의해서는 직접 알고 있는 회원들은 1의 거리를 갖는다는 키워드를 파악하면 됩니다.
따라서 직접 연결되지 않고 타인을 통해서 알게 되는 사람과의 거리는 둘을 연결해주는 사람의 수 +1 점수를 갖게 됩니다.

예를 들어 1 → 2의 점수를 정할 때, 1과 2가 직접 연결되지 않았으면 다른 사람을 통해서 점수를 계산해야 합니다.
1 → 3 → 2 와 같은 경우는 2의 점수를 갖고
1 → 4 → 5 → 2 와 같은 경우는 3의 점수를 갖습니다.
하지만 문제에서는 가까운 정도를 원하기에 최솟값인 2의 점수가 1 → 2의 점수가 됩니다.
따라서 이 문제에서는 모든 회원과 다른 모든 회원들과의 최단 거리를 구해줘야 하므로 플로이드 와샬이 적합한 알고리즘이었습니다!

예를 들어 어느 회원이 다른 모든 회원과 친구이면, 이 회원의 점수는 1점이다.
어느 회원의 점수가 2점이면, 다른 모든 회원이 친구이거나 친구의 친구임을 말한다.
또한 어느 회원의 점수가 3점이면, 다른 모든 회원이 친구이거나, 친구의 친구이거나, 친구의 친구의 친구임을 말한다.

해당 조건에 의해서 한 회원의 최단 거리 정보들 중에서 최댓값이 해당 회원의 최종 점수가 됩니다.

여기서 이해 안 가시는 부분 추가적으로 작성해주시면 도움드릴게요!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

두 분 모두 방향 잡아주셔서 정말 감사합니다 ㅠㅠ

제가 예시를 잘못 이해하고 있었네요

다른 모든 회원이 친구이거나, 친구의 친구이거나, 친구의 친구의 친구임을 말한다.

예시를 잘못 이해해서.. 어떤 경우에 2점인지 3점인지.. 이해를 못하고 있었습니다
가까운 정도 -> 거리로 생각 해야되는 문제였군요...!

점수 : 특정 회원이 다른 모든 회원과 연결되위해 가까운 정도 ->최솟값
회장 : 점수가 가장 낮은 사람(가장 가까운 거리에 있는 사람) -> 최단거리
조건 : 몇 사람을 통하면 모두가 서로알 수 있다 = 모든 회원(노드)가 연결되어 있음
=> 모든 회원과 다른 모든 회원들과의 최단 거리 구하기 -> 플로이드

질문 드리고 또 잘못된 방향으로 이해하고 있었습니다..

1->3->2 2점, 1->4->5->2 3점, 그럼 1->2 관계는 3점 인가?? 이러고 있었습니다ㅎ.. 점수가 최솟값이었군요

제가 올바르게 이해한게 맞을까요.....?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

거리는 최딘 거리로 하지만 그 해당 회원의 점수는 최단 거리들 중 최댓값으로
입니다!

32 changes: 32 additions & 0 deletions BOJ/10001-15000번/JW_13164.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import java.util.Arrays;

public class JW_13164 {

public static void main(String[] args) throws Exception {
int n = read(), k = read();
int[] arr = new int[n];
for (int i = 0; i < n; i++)
arr[i] = read();
// 다음 사람과 같은 조가 되었을 때 비용을 저장할 배열
int[] diff = new int[n - 1];
// 다음 사람과의 키 차이 계산
for (int i = 0; i < n - 1; i++)
diff[i] = arr[i + 1] - arr[i];
// 오름차 순으로 정렬
Arrays.sort(diff);
int answer = 0;
// 키 차이가 별로 안나는 조합은 묶어도 됨
for (int i = 0; i < n - k; i++)
answer += diff[i];
System.out.println(answer);
}

private static int read() throws Exception {
int c, n = System.in.read() & 15;
while ((c = System.in.read()) >= 48)
n = (n << 3) + (n << 1) + (c & 15);
if (c == 13)
System.in.read();
return n;
}
}
77 changes: 77 additions & 0 deletions BOJ/10001-15000번/JW_14620.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
public class JW_24620 {
static int n;
static int[][] board;
static boolean[][] visited;
// 현재 위치 + 상하좌우를 확인하기 위한 변화량
static int[] dy = { 0, 1, -1, 0, 0 };
static int[] dx = { 0, 0, 0, 1, -1 };
Comment on lines +6 to +7
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

방향벡터에 (0, 0)도 추가해주신 점 좋은 것 같습니다 👍

static int min = Integer.MAX_VALUE;

public static void main(String[] args) throws Exception {
n = read();
board = new int[n][n];
visited = new boolean[n][n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
board[i][j] = read();
// 완전 탐색
recursive(0, 0, 0);
System.out.println(min);
}

private static void recursive(int depth, int total, int p) {
// 3개를 다 심었다면 최솟값 갱신
if (depth == 3) {
min = Math.min(min, total);
return;
}
// 심을 수 있는 위치 찾기
for (int i = p; i < n * n; i++) {
int y = i / n, x = i % n;
boolean isPossible = true;
// 모든 유효성을 통과하는지 확인
for (int j = 0; j < 5; j++) {
int ny = y + dy[j];
int nx = x + dx[j];
if (!isValid(ny, nx)) {
isPossible = false;
}
}
// 심을 수 있는 위치일 경우
if (isPossible) {
int sum = 0;
// 방문 처리
for (int j = 0; j < 5; j++) {
int ny = y + dy[j];
int nx = x + dx[j];
visited[ny][nx] = true;
sum += board[ny][nx];
}
// 다음 깊이의 재귀 진행
recursive(depth + 1, total + sum, i + 1);

// 백 트래킹
for (int j = 0; j < 5; j++) {
int ny = y + dy[j];
int nx = x + dx[j];
visited[ny][nx] = false;
}
}
}
}

// 경계 체크 및 방문하지 않았는지 유효성 검증
private static boolean isValid(int y, int x) {
return 0 <= y && y < n && 0 <= x && x < n && !visited[y][x];
}

// 빠른 입력 함수
private static int read() throws Exception {
int c, n = System.in.read() & 15;
while ((c = System.in.read()) >= 48)
n = (n << 3) + (n << 1) + (c & 15);
if (c == 13)
System.in.read();
return n;
}
}
92 changes: 92 additions & 0 deletions CodeTree/2017-2018년/JW_전투_로봇.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class JW_전투_로봇 {

static int n;
static int[][] board;
static int[] dy = { -1, 1, 0, 0 };
static int[] dx = { 0, 0, -1, 1 };
static int level = 2, count = 0, totalMove = 0;

public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine());
board = new int[n][n];
int sy = 0, sx = 0;
for (int i = 0; i < n; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
for (int j = 0; j < n; j++) {
int p = Integer.parseInt(st.nextToken());
// 초기 로봇 위치 저장
if (p == 9) {
sy = i;
sx = j;
} else if (p != 0) {
board[i][j] = p;
}
}
}
Deque<int[]> dq = new ArrayDeque<>();
boolean[][] visited = new boolean[n][n];
dq.offer(new int[] { 0, sy, sx });
// BFS
while (!dq.isEmpty()) {
// 처리할 수 있는 몬스터들의 위치에 따라 우선 순위 부여
PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[0] != o2[0] ? o1[0] - o2[0] : o1[1] - o2[1]);
// 깊이 별로 BFS 진행
int t = dq.size();
while (t-- > 0) {
int[] cur = dq.poll();
int y = cur[1], x = cur[2];
if (visited[y][x])
continue;
visited[y][x] = true;
// 해당 레벨에서 잡을 수 있는 몬스터가 존재한다면 우선 순위 큐에 저장
if (isCatch(y, x)) {
pq.offer(cur);
continue;
}
for (int i = 0; i < 4; i++) {
int ny = y + dy[i], nx = x + dx[i];
if (isValid(ny, nx) && !visited[ny][nx]) {
dq.offer(new int[] { cur[0] + 1, ny, nx });
}
}
}
// 해당 깊이에서 처리할 수 있는 몬스터를 1개 이상 발견했다면
if (!pq.isEmpty()) {
// 우선 순위가 가장 높은 몬스터를 처리
int[] cur = pq.poll();
totalMove += cur[0];
int y = cur[1], x = cur[2];
board[y][x] = 0;
// 없앤 몬스터의 수 갱신과 레벨 업
count++;
if (count == level) {
level++;
count = 0;
}
// 큐를 비우고 해당 위치에서 다시 시작
dq.clear();
dq.offer(new int[] { 0, y, x });
visited = new boolean[n][n];
}
}
System.out.println(totalMove);
}

// 몬스터를 없앨 수 있는지 확인하는 함수
private static boolean isCatch(int y, int x) {
return 0 < board[y][x] && board[y][x] < level;
}

// 경계 체크 함수
private static boolean isValid(int y, int x) {
return 0 <= y && y < n && 0 <= x && x < n && board[y][x] <= level;
}
}
40 changes: 40 additions & 0 deletions Programmers/Level3/JW_118668.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import java.util.Arrays;

class JW_118668 {
public int solution(int alp, int cop, int[][] problems) {
// 모든 문제를 풀기 위한 최솟값을 찾기 위해 초기화
int maxAlp = alp, maxCop = cop;
for (int i = 0; i < problems.length; i++) {
maxAlp = Math.max(maxAlp, problems[i][0]);
maxCop = Math.max(maxCop, problems[i][1]);
}
// DP 배열 초기화
int[][] dp = new int[maxAlp + 1][maxCop + 1];
for (int i = 0; i < maxAlp + 1; i++)
Arrays.fill(dp[i], Integer.MAX_VALUE >> 2);
dp[alp][cop] = 0;

// DP
// 현재 값 기준으로 다음 값 결정하기
for (int i = alp; i < maxAlp + 1; i++) {
for (int j = cop; j < maxCop + 1; j++) {
// 알고력 증가
if (i < maxAlp)
dp[i + 1][j] = Math.min(dp[i + 1][j], dp[i][j] + 1);
// 코딩력 증가
if (j < maxCop)
dp[i][j + 1] = Math.min(dp[i][j + 1], dp[i][j] + 1);
// 문제 풀기
for (int[] problem : problems)
// 풀 수 있는 문제라면
if (i >= problem[0] && j >= problem[1]) {
// 능력치 증가
int nextAlp = Math.min(maxAlp, i + problem[2]);
int nextCop = Math.min(maxCop, j + problem[3]);
dp[nextAlp][nextCop] = Math.min(dp[nextAlp][nextCop], dp[i][j] + problem[4]);
}
Comment on lines +30 to +35
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 문제를 풀었을 때 코딩력, 알고력 중 어떤걸 올릴지, 둘다 올릴지에 대한 각 경우를 생각해서 if 조건문으로 나눴는데,
최대 코딩력/알고력을 기준으로 대소비교(Math.min)를 하면 위 경우를 한번에 처리할 수 있군요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아요! 최대로 넘어가지 않는 범위로 조정해주는 게 편해요!

}
}
return dp[maxAlp][maxCop];
}
}
Loading