2025.04.17 ~ 04.18
선택의 순간마다 당장 가장 좋아 보이는 최적의 선택 상황만을 쫓아 최종적인 해답에 도달하는 알고리즘
큰 수부터 반복
int count = 0;
while(true) {
<!-- 5키로 봉지로 정확히 나누는 경우 -->
if(n % 5 == 0) {
return n / 5 + count;
} else if (n < 0) { <!-- 3 or 5 조합으로 해결 안되는 상황-->
return -1;
} else if(n == 0) { <!-- 3키로 봉지로만 해결 (없어도 되는 구분) -->
return count;
}
<!-- 5키로로 나누어 떨어지지 않는다면 3키로 봉지 사용 해보기 -->
n = n - 3;
count++;
}
큰 수부터 반복
int count = 0; <!-- 필요한 동전의 개수 -->
<!-- 큰 동전부터 사용해볼 수 있도록 배열을 내림차순으로 반복 -->
for(int i = N - 1; i >= 0; i--) {
<!-- 해당 동전을 사용할 가치가 있을 경우, 값보다 동전이 작아진 경우 -->
if(coins[i] <= K) {
<!-- 현재의 동전으로 최대 지불할 수 있는 금액 (동전의 수) -->
count += K / coins[i];
<!-- 지불하고 남은 금액을 다음 동전 확인을 위해 K에 대입 -->
K = K % coins[i];
}
}
return count;
시작시간이 제일 빠른(최소) 것부터 반복
<!-- 기본적인 조건은 종료 시간이 빠른 회의 순서로 데이터 정렬 -->
<!-- time 배열 : 각 회의의 [시작시간, 종료 시간] -->
Arrays.sort(time, (t1, t2) -> {
<!-- 종료 시간이 같은 회의가 있다면 시작 시간이 빠른 순서로 정렬 -->
<!-- 경계 시간에 있는 회의가 올바르게 처리 되도록 주는 기준 -->
if(t1[1] == t2[1]) return t1[0] - t2[0];
<!-- 오름차순 정렬 -->
<!--
결과가 음수 → t1이 t2보다 먼저 와야 함
결과가 양수 → t2가 먼저 와야 함
결과가 0 → 두 요소는 같은 순서로 간주
-->
return t1[1] - t2[1];
});
int endTime = 0; <!-- 직전 회의가 끝난 시간을 담아둘 변수 -->
int count = 0; <!-- 회의가 배정 된 개수 -->
<!-- time 배열 안에 있는 회의를 반복하며 회의 시간표에 넣을지 확인 -->
for(int i = 0; i < N; i++) {
<!-- 직전 회의가 끝나는 시간과 일치하거나 그 이후에 시작 되는지 확인 -->
if(time[i][0] >= endTime) {
count++;
endTime = time[i][1]; <!-- 이후 회의 확인을 위해 종료 시간 수정 -->
}
}
return count;
그래프의 한 정점에서 모든 정점까지의 최단거리를 구하는 알고리즘





static int n, m, start; <!-- 정점 개수, 간선 개수, 시작 정점 -->
static int[] dis; <!-- 다른 노드까지의 거리를 저장할 배열 -->
static class Edge implements Comparable<Edge> {
int ver; <!-- 해당 간선이 연결 된 정점 -->
int cost; <!-- 가중치 -->
Edge(int ver, int cost) {
this.ver = ver;
this.cost = cost;
}
@Override
public int compareTo(Edge o) {
return this.cost - o.cost; <!-- 비용이 낮을수록 우선순위가 높아짐 (오름차순) -->
}
}
public static String solution(String input) throws IOException {
BufferedReader br = new BufferedReader(new StringReader(input));
StringTokenizer st = new StringTokenizer(br.readLine());
n = Integer.parseInt(st.nextToken()); <!-- 정점의 개수 -->
m = Integer.parseInt(st.nextToken()); <!-- 간선의 개수 -->
start = Integer.parseInt(st.nextToken()); <!-- 시작할 정점 -->
<!-- 인접 리스트 형태로 그래프 초기화 -->
ArrayList<ArrayList<Edge>> graph = new ArrayList<>();
for (int i = 0; i <= n; i++) {
graph.add(new ArrayList<>());
}
<!-- 각 노드의 가중치 기록할 배열, 0번은 사용하지 않음 -->
dis = new int[n + 1];
<!-- 아직 거리가 판단 되지 않은 경우에는 Integer 최대 값으로 채워 둔다 -->
Arrays.fill(dis, Integer.MAX_VALUE);
for(int i = 0; i < m; i++){
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken()); <!-- 시작 정점 -->
int b = Integer.parseInt(st.nextToken()); <!-- 도착 정점 -->
int c = Integer.parseInt(st.nextToken()); <!-- 가중치 -->
graph.get(a).add(new Edge(b, c));
}
<!-- 우선순위 큐에 Edge가 담겼을 때 우선 순위는 가중치가 낮은 순서로 정해진다. -->
<!-- 우선순위 큐는 들어온 순서와 상관없이
우선순위가 높은 값(보통 작은 값부터)이 먼저 나간다 -->
<!-- PriorityQueue<Integer> → 기본 타입 int니까 자동으로 오름차순 정렬 -->
<!-- PriorityQueue<Edge> → 사용자 정의 클래스는 어떤 기준으로 정렬해야 할지
compareTo()으로 정렬 기준 선언 -->
PriorityQueue<Edge> pq = new PriorityQueue<>();
pq.offer(new Edge(start, 0));
<!-- 시작 지점은 자기 자신이므로 거리0으로 시작 -->
dis[start] = 0;
while(!pq.isEmpty()) {
Edge tmp = pq.poll();
int now = tmp.ver;
int nowCost = tmp.cost;
<!-- 이미 더 짧은 경로가 있으면 무시 -->
if(nowCost > dis[now]) continue;
<!-- 기준 정점과 연결 된 이웃 정점을 큐에 추가 하는 반복문 -->
for(Edge edge : graph.get(now)) {
<!-- 거리를 기록해두는 배열에 저장 된 값이
현재 비용과 간선을 타고 가는 비용을 더한 값 보다 크다면 -->
<!-- 현재까지 거리 + 간선의 가중치 < 기존 저장 거리 -->
if(dis[edge.ver] > nowCost + edge.cost) {
<!-- 새로운 루트로 업데이트 한다. -->
dis[edge.ver] = nowCost + edge.cost;
<!-- 새로운 경로 추가 -->
pq.offer(new Edge(edge.ver, nowCost + edge.cost));
}
}
}
StringBuilder sb = new StringBuilder();
<!-- 시작 정점이 1번이라 가정하고, 2번 정점부터 출력 -->
for(int i = 2; i < dis.length; i++) {
if(dis[i] != Integer.MAX_VALUE) {
sb.append(dis[i]);
} else {
sb.append("impossible");
}
sb.append(" ");
}
return sb.toString().trim();
}
집합 간의 연산을 효율적으로 처리하기 위해 설계 된 데이터 구조
<!-- 부모(root) 노드 저장할 배열 -->
static int[] parent;
<!-- 특정 원소가 속한 집합을 찾는 연산 -->
static int find(int x) {
<!-- 집합의 대표 원소 (루트 노드) 를 찾고
대표 원소를 알면 두 원소가 같은 집합에 속하는지 알 수 있다. -->
if(parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
<!-- 두 개의 집합을 하나로 합치는 연산
두 집합의 대표 원소를 비교하여 두 집합이 연결 되도록 한다. -->
static void union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
// 앞쪽 원소를 root로 하는 기준으로 작성하면
if(rootX != rootY) parent[rootY] = rootX;
}
BufferedReader br = new BufferedReader(new StringReader(input));
StringTokenizer st;
<!-- 학생 수와 관계 읽어오기 -->
st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken()); <!-- 학생 수 -->
int M = Integer.parseInt(st.nextToken()); <!-- 제공 된 학생 쌍 -->
<!-- parent 배열 초기화 (처음에는 각각의 요소가 별도의 집합으로 구성) -->
parent = new int[N + 1];
for(int i = 1; i <= N; i++) {
parent[i] = i;
}
<!-- M개의 학생 쌍 처리 -->
for(int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
union(a, b); <!-- a 학생과 b 학생을 같은 집합으로 만듦 -->
System.out.println("union(" + a + "," + b + ")");
System.out.println("parent : " + Arrays.toString(parent));
}
<!-- 마지막 쌍의 친구 관계 확인 -->
st = new StringTokenizer(br.readLine());
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
if(find(x) == find(y)) {
return "YES";
} else {
return "NO";
}
주어진 그래프의 모든 정점을 연결하는 부분 그래프 중 가중치의 값이 최소인 트리





최소 가중치의 합
static int[] parent;
<!-- 간선 정보 클래스 -->
static class Edge implements Comparable<Edge> {
int u, v, weight;
Edge(int u, int v, int weight) {
this.u = u; <!-- 정점 1 -->
this.v = v; <!-- 정점 2 -->
this.weight = weight; <!-- 가중치 -->
}
@Override
public int compareTo(Edge o) { <!-- 간선 정렬 시 가중치 오름차순 적용 -->
return this.weight - o.weight;
}
}
<!-- 특정 원소가 속한 집합을 찾는 연산
이를 통해 집합의 대표 원소(루트 노드)를 찾고, 대표 원소를 알면
두 원소가 같은 집합에 속하는지 확인할 수 있다. -->
static int find(int x) {
if(parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
<!-- 두 개의 집합을 하나로 합치는 연산.
두 집합의 대표 원소를 비교하여 두 집합이 연결 되도록 함. -->
static void union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
<!-- 앞쪽 원소를 root로 하는 기준으로 작성 -->
if(rootX != rootY) {
parent[rootY] = rootX;
}
}
BufferedReader br = new BufferedReader(new StringReader(input));
StringTokenizer st = new StringTokenizer(br.readLine());
int V = Integer.parseInt(st.nextToken());
int E = Integer.parseInt(st.nextToken());
Edge[] edges = new Edge[E];
for (int i = 0; i < E; i++) {
st = new StringTokenizer(br.readLine());
int u = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
int weight = Integer.parseInt(st.nextToken());
edges[i] = new Edge(u, v, weight);
}
<!-- 유니온 초기화 -->
parent = new int[V + 1];
for(int i = 1; i <= V; i++) {
parent[i] = i;
}
<!-- 1. 가중치 오름차순 정렬 (기준은 내부에 정의 되어 있음) -->
Arrays.sort(edges);
long totalWeight = 0L;
<!-- 2. 가중치 작은 간선부터 선택해나가는 작업 -->
for(Edge edge : edges) {
<!-- 각각의 정점이 연결 되어 있는지 확인 -->
if(find(edge.u) != find(edge.v)) {
<!-- 연결 되어 있지 않다면 정점을 연결 -->
union(edge.u, edge.v);
totalWeight += edge.weight;
}
}
return totalWeight;
시작 정점에서 시작하여, 최소 가중치 간선을 추가하면서 최소 스패닝 트리를 확장하는 방법
static class Edge {
int vertex, weight;
Edge(int vertex, int weight) {
this.vertex = vertex;
this.weight = weight;
}
}
BufferedReader br = new BufferedReader(new StringReader(input));
StringTokenizer st = new StringTokenizer(br.readLine());
int V = Integer.parseInt(st.nextToken());
int E = Integer.parseInt(st.nextToken());
<!-- 인접 리스트 그래프 초기화 -->
List<Edge>[] graph = new ArrayList[V + 1];
for (int i = 1; i <= V; i++) {
graph[i] = new ArrayList<>();
}
<!-- 간선 정보 입력 (양방향) -->
for (int i = 0; i < E; i++) {
st = new StringTokenizer(br.readLine());
int u = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
int weight = Integer.parseInt(st.nextToken());
graph[u].add(new Edge(v, weight));
graph[v].add(new Edge(u, weight));
}
boolean[] visited = new boolean[V + 1];
<!-- 우선순위 큐: 비용이 낮은 간선이 먼저 나오도록 설정 -->
PriorityQueue<Edge> pq = new PriorityQueue<>(Comparator.comparingInt(e -> e.weight));
<!-- 정점 1에서 시작 -->
pq.add(new Edge(1, 0));
long totalWeight = 0;
while(!pq.isEmpty()) {
Edge edge = pq.poll();
int currentVertex = edge.vertex;
int currentWeight = edge.weight;
if(visited[currentVertex]) continue;
visited[currentVertex] = true;
totalWeight += currentWeight;
<!-- 현재 정점과 연결된 모든 간선을 큐에 추가 -->
for(Edge neighbor : graph[currentVertex]) {
if(!visited[neighbor.vertex]) {
pq.offer(neighbor);
}
}
}
return totalWeight;