Skip to content

[박준서-17주차 알고리즘 스터디] #100

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions 박준서/17주차/[Baekjoon-1477]휴게소세우기.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import java.io.*;
import java.util.*;

public class Main {
private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
private static StringTokenizer st = new StringTokenizer("");

private static void readLine() throws Exception {
st = new StringTokenizer(br.readLine());
}

static String nextToken() throws Exception {
while (!st.hasMoreTokens())
readLine();
return st.nextToken();
}

private static int nextint() throws Exception {
return Integer.parseInt(nextToken());
}

private static void bwEnd() throws Exception {
bw.flush();
bw.close();
}

private static int N; // 기존 휴게소 개수
private static int M; // 추가할 휴게소 개수
private static int L; // 고속도로 길이
private static ArrayList<Integer> arr; // 휴게소 위치 저장 리스트
private static ArrayList<Integer> terms; // 휴게소 간 거리 저장 리스트

public static void main(String args[]) throws Exception {
init();
bw.write(find() + "\n"); // 결과 계산 및 출력
bwEnd(); // 출력 스트림 닫기
}

public static void init() throws Exception {
N = nextint(); // 기존 휴게소 개수
M = nextint(); // 추가할 휴게소 개수
L = nextint(); // 고속도로 길이
terms = new ArrayList<>(); // 휴게소 간 거리를 저장할 리스트

if (N == 0) {
terms.add(L); // 시작점부터 끝점까지의 거리만 저장
return;
}

arr = new ArrayList<>(); // 휴게소 위치를 저장할 리스트
for (int i = 0; i < N; i++)
arr.add(nextint()); // 각 휴게소 위치 입력
arr.sort(Comparator.naturalOrder()); // 위치 오름차순 정렬

// 휴게소 간 거리 계산
terms.add(arr.get(0) - 0); // 시작점부터 첫 휴게소까지의 거리
for (int i = 1; i < N; i++)
terms.add(arr.get(i) - arr.get(i - 1)); // 인접한 휴게소 간 거리
terms.add(L - arr.get(N - 1)); // 마지막 휴게소부터 끝점까지의 거리
}

// 최적의 휴게소 간 최대 거리를 찾는 메소드
public static int find() {
// 특수 케이스: 모든 위치에 휴게소가 있는 경우
if (L - N - M == 1)
return 1;

// 가능한 모든 거리 값에 대해 탐색 (2부터 L까지)
loop: for (int i = 2; i <= L; i++) {
int cnt = 0; // 추가로 필요한 휴게소 개수

// 각 구간마다 필요한 휴게소 개수 계산
for (int it : terms) {
// (구간 길이 - 1) / i: 해당 구간에 추가로 필요한 휴게소 수
cnt += (it - 1) / i;

// 필요한 휴게소 수가 M을 초과하면 현재 거리 i는 불가능
if (cnt > M)
continue loop; // 다음 거리 값으로 넘어감
}

return i; // 조건을 만족하는 최소 거리 반환
}
return 0; // 해를 찾지 못한 경우 (실제로는 발생하지 않음)
}
}
83 changes: 83 additions & 0 deletions 박준서/17주차/[Baekjoon-2138]전구와스위치.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import java.io.*;
import java.util.*;

public class Main {
private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
private static StringTokenizer st = new StringTokenizer("");

private static void readLine() throws Exception {
st = new StringTokenizer(br.readLine());
}

static String nextToken() throws Exception {
while (!st.hasMoreTokens())
readLine();
return st.nextToken();
}

private static int nextint() throws Exception {
return Integer.parseInt(nextToken());
}

private static void bwEnd() throws Exception {
bw.flush();
bw.close();
}

private static int N, ans = Integer.MAX_VALUE; // 전구 개수와 최소 스위치 조작 횟수
private static char[] arr1, arr2; // 초기 상태와 목표 상태 배열

public static void main(String args[]) throws Exception {
init(); // 초기화
blob(0); // 첫 번째 스위치를 누르지 않는 경우
flip(arr1, 0); // 첫 번째 스위치를 누른 상태로 시작
blob(1); // 첫 번째 스위치를 누르는 경우

if (ans == Integer.MAX_VALUE)
bw.write("-1\n"); // 불가능한 경우
else
bw.write(ans + "\n"); // 최소 스위치 조작 횟수
bwEnd();
}

public static void init() throws Exception {
N = nextint(); // 전구 개수
arr1 = nextToken().toCharArray(); // 초기 상태
arr2 = nextToken().toCharArray(); // 목표 상태
}

// 그리디 알고리즘으로 전구를 목표 상태로 변경하는 메소드
public static void blob(int st) {
int cnt = st; // 스위치 조작 횟수 (첫 번째 스위치 조작 여부로 초기화)
char[] tmp = arr1.clone(); // 초기 상태 복사

// 첫 번째 전구부터 순차적으로 처리
for (int i = 1; i < N; i++) {
// 이전 전구가 목표 상태와 다르면 현재 위치의 스위치를 누름
if (tmp[i - 1] != arr2[i - 1]) {
flip(tmp, i); // i번째 스위치 누름
cnt++; // 조작 횟수 증가
}
}

// 모든 전구가 목표 상태와 일치하는지 확인
if (check(tmp))
ans = Math.min(ans, cnt); // 최소 조작 횟수 갱신
}

// idx 위치의 스위치를 눌러 전구 상태를 변경하는 메소드
public static void flip(char[] arr, int idx) {
// idx와 그 인접한 전구(idx-1, idx+1)의 상태를 반전
for (int i = Math.max(0, idx - 1); i <= Math.min(idx + 1, N - 1); i++)
arr[i] = arr[i] == '0' ? '1' : '0'; // 0->1, 1->0으로 변경
}

// 모든 전구가 목표 상태와 일치하는지 확인하는 메소드
public static boolean check(char[] arr) {
for (int i = 0; i < N; i++)
if (arr[i] != arr2[i])
return false; // 하나라도 다르면 false
return true; // 모두 일치하면 true
}
}
224 changes: 224 additions & 0 deletions 박준서/17주차/[Baekjoon-23291]어항정리.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import java.io.*;
import java.util.*;

public class Main {
private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
private static StringTokenizer st = new StringTokenizer("");

private static void readLine() throws Exception {
st = new StringTokenizer(br.readLine());
}

static String nextToken() throws Exception {
while (!st.hasMoreTokens())
readLine();
return st.nextToken();
}

private static int nextint() throws Exception {
return Integer.parseInt(nextToken());
}

private static void bwEnd() throws Exception {
bw.flush();
bw.close();
}

private static final int[] dx = { 0, 1, 0, -1 };
private static final int[] dy = { -1, 0, 1, 0 };

private static int N, K, ans; // N: 어항 수, K: 목표 차이, ans: 연산 횟수
private static ArrayList<Integer> fishTanks; // 어항의 물고기 수를 저장하는 리스트

public static void main(String args[]) throws Exception {
init();
final int[][] rollMatrix = roll();
final int[][] horizenalMatrix = flipHorizenal();

do {
// 물고기 수가 가장 적은 어항에 물고기 한 마리 추가
addFish(fishTanks);
// 말아 올린 상태에서 물고기 수 조절
spread(rollMatrix);
// 수평으로 접은 상태에서 물고기 수 조절
spread(horizenalMatrix);
ans++;
} while (!check(fishTanks)); // 물고기 수 차이가 K 이하가 될 때까지 반복

bw.write(ans + "\n");
bwEnd();
}

// 입력을 받아 초기화하는 메소드
public static void init() throws Exception {
N = nextint(); // 어항의 수
K = nextint(); // 목표 물고기 수 차이

fishTanks = new ArrayList<>(); // 어항 리스트 초기화
for (int i = 0; i < N; i++)
fishTanks.add(nextint()); // 각 어항의 물고기 수 입력
}

// 어항을 말아 올리는 배치 행렬을 생성하는 메소드
public static int[][] roll() {
int cnt = 4; // 이미 처리된 어항 수
int next = 2, c = 1; // next: 다음에 필요한 어항 수, c: 회전 카운터
int[][] ret = { { 1, 0 }, { 2, 3 } }; // 초기 배치 (2x2 행렬)

// 남은 어항이 충분할 때까지 말아 올리기 반복
while (N - cnt >= next) {
int H = ret.length;
int W = ret[0].length + 1;
int[][] tmp = new int[W][H]; // 새 배치 행렬

// 기존 행렬을 90도 회전
for (int i = 0; i < H; i++)
for (int j = 0; j < W - 1; j++)
tmp[j][H - i - 1] = ret[i][j];

// 새로운 어항 추가
for (int i = 0; i < H; i++)
tmp[W - 1][i] = cnt++;

if (++c == 2) { // 두 번 회전할 때마다
next++; // 필요한 어항 수 증가
c = 0;
}
ret = tmp;

if (N - cnt < next) {
break;
}

ret = tmp;
}

// 남은 어항 처리
int rest = N - cnt;
int[][] tmp = new int[ret.length][];
for (int i = 0; i < ret.length - 1; i++) {
int[] clon = new int[ret[0].length];
for (int j = 0; j < ret[0].length; j++)
clon[j] = ret[i][j];
tmp[i] = clon;
}
// 마지막 행에 남은 어항 추가
tmp[ret.length - 1] = new int[ret[0].length + rest];
for (int i = 0; i < ret[0].length; i++)
tmp[ret.length - 1][i] = ret[ret.length - 1][i];
for (int i = 0; i < rest; i++) {
tmp[ret.length - 1][ret[0].length + i] = cnt++;
}

ret = tmp;
return ret;
}

// 수평으로 접는 배치 행렬을 생성하는 메소드
public static int[][] flipHorizenal() {
int[][] ret = new int[4][N / 4]; // 4행으로 구성된 배치 행렬
int cnt = 0;

// 어항을 4단계로 접어서 배치
for (int i = N / 4 - 1; i >= 0; i--)
ret[2][i] = cnt++;
for (int i = 0; i < N / 4; i++)
ret[1][i] = cnt++;
for (int i = N / 4 - 1; i >= 0; i--)
ret[0][i] = cnt++;
for (int i = 0; i < N / 4; i++)
ret[3][i] = cnt++;

return ret;
}

// 물고기 수가 가장 적은 어항에 물고기 한 마리 추가하는 메소드
public static void addFish(ArrayList<Integer> arr) {
int min = Integer.MAX_VALUE;
for (int it : arr)
min = Math.min(it, min); // 최소값 찾기

// 최소값인 어항에만 물고기 추가
for (int i = 0; i < arr.size(); i++)
arr.set(i, arr.get(i) + (arr.get(i) == min ? 1 : 0));
}

// 물고기 수 조절 메소드
public static void spread(int[][] in) {
ArrayList<Integer> ret = new ArrayList<>();
for (int it : fishTanks)
ret.add(it); // 현재 어항 상태 복사

// 인접한 어항 간 물고기 수 조절
for (int i = 0; i < in.length; i++) {
for (int j = 0; j < in[i].length; j++) {
int nowIdx = in[i][j]; // 현재 어항 인덱스
int now = fishTanks.get(nowIdx); // 현재 어항의 물고기 수

// 상, 우 방향만 검사 (중복 방지)
for (int k = 0; k < 2; k++) {
int ny = i + dy[k];
int nx = j + dx[k];
if (OOB(in, ny, nx)) // 범위 밖이면 건너뛰기
continue;

int nextIdx = in[ny][nx]; // 인접 어항 인덱스
int next = fishTanks.get(nextIdx); // 인접 어항의 물고기 수
int num = Math.abs(next - now) / 5; // 이동할 물고기 수 계산

// 물고기 이동
if (num != 0) {
if (now > next) { // 현재 어항의 물고기가 더 많으면
ret.set(nextIdx, ret.get(nextIdx) + num); // 인접 어항에 추가
ret.set(nowIdx, ret.get(nowIdx) - num); // 현재 어항에서 감소
} else { // 인접 어항의 물고기가 더 많으면
ret.set(nextIdx, ret.get(nextIdx) - num); // 인접 어항에서 감소
ret.set(nowIdx, ret.get(nowIdx) + num); // 현재 어항에 추가
}
}
}
}
}

reshape(in, ret); // 어항 재배치
fishTanks = ret; // 결과 저장
}

// 어항을 일렬로 재배치하는 메소드
public static void reshape(int[][] in, ArrayList<Integer> arr) {
ArrayList<Integer> tmp = new ArrayList<>();
for (int i = 0; i < arr.size(); i++)
tmp.add(arr.get(i)); // 현재 상태 복사
arr.clear(); // 리스트 비우기

// 배치 행렬에 따라 어항을 일렬로 재배치
for (int j = 0; j < in[in.length - 1].length; j++)
for (int i = in.length - 1; i >= 0; i--)
if (!OOB(in, i, j)) // 유효한 위치면
arr.add(tmp.get(in[i][j])); // 해당 어항 추가
}

// 배열 범위를 벗어나는지 확인하는 메소드
public static boolean OOB(int[][] in, int y, int x) {
if (y < 0 || x < 0 || y >= in.length)
return true; // 기본 범위 체크

// 불규칙한 배열 형태 처리
if ((y < in.length - 1 && x >= in[0].length) ||
(y == in.length - 1 && x >= in[in.length - 1].length))
return true;

return false;
}

// 물고기 수 차이가 K 이하인지 확인하는 메소드
public static boolean check(ArrayList<Integer> FT) {
int min = 10000, max = 0;
for (int it : FT) {
min = Math.min(min, it); // 최소값 갱신
max = Math.max(max, it); // 최대값 갱신
}
return max - min <= K; // 차이가 K 이하면 true 반환
}
}
Loading