diff --git "a/java/src/feb/week3/boj/\353\221\220\354\232\251\354\225\241_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/boj/\353\221\220\354\232\251\354\225\241_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..d409d81 --- /dev/null +++ "b/java/src/feb/week3/boj/\353\221\220\354\232\251\354\225\241_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,51 @@ +package feb.week3.boj; + +import java.util.*; +import java.io.*; + +public class 두용액_박재환 { + static BufferedReader br; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + init(); + br.close(); + } + static StringTokenizer st; + static int n; + static int[] arr; + static void init() throws IOException { + n = Integer.parseInt(br.readLine().trim()); + arr = new int[n]; + st = new StringTokenizer(br.readLine().trim()); + for(int i=0; i sum) { + result = new Result(a, b, sum); + } + if(sum == 0) break; // 더 이상 최적값을 찾을 수 없음 + // 포인터 위치 변경 + if(a+b > 0) r--; // 두 수의 합이 양수 -> 큰 값을 줄여봄 + else l++; // 두 수의 합이 음수 -> 작은 값을 줄여봄 + } + return result; + } +} diff --git "a/java/src/feb/week3/boj/\354\202\254\355\203\225\354\203\201\354\236\220_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/boj/\354\202\254\355\203\225\354\203\201\354\236\220_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..defe703 --- /dev/null +++ "b/java/src/feb/week3/boj/\354\202\254\355\203\225\354\203\201\354\236\220_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,132 @@ +package feb.week3.boj; + +import java.util.*; +import java.io.*; + +public class 사탕상자_박재환 { + static BufferedReader br; + static StringBuilder sb; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + sb = new StringBuilder(); + init(); + br.close(); + System.out.println(sb); + } + /** + * 사탕의 맛 1 ~ 1,000,000 (1이 가장 맛있음) + * 몇 번째로 맛있는 사탕을 꺼내주곤 한다. + */ + static final int OUT = 1; + static final int IN = 2; + + static class Command { + int cmd; + int b; + int c; + + Command(int cmd, int b) { + /** + * 사탕을 빼는 경우 + */ + this.cmd = cmd; + this.b = b; + } + Command(int cmd, int b, int c) { + /** + * 사탕을 넣는 경우 + */ + this.cmd = cmd; + this.b = b; + this.c = c; + } + } + static StringTokenizer st; + static Queue commands; + static TreeSet values; + static void init() throws IOException { + commands = new ArrayDeque<>(); + values = new TreeSet<>(Integer::compare); + + int n = Integer.parseInt(br.readLine().trim()); // 사탕 상자에 손을 댄 횟수 + while(n-- > 0) { + st = new StringTokenizer(br.readLine().trim()); + int cmd = Integer.parseInt(st.nextToken()); + Command command = null; + if(cmd == IN) { + int b = Integer.parseInt(st.nextToken()); + int c = Integer.parseInt(st.nextToken()); + command = new Command(IN, b, c); + values.add(b); // c가 음수가 될 수도 있으니, 중복되지 않게 + } else if(cmd == OUT) { + int b = Integer.parseInt(st.nextToken()); + command = new Command(OUT, b); + } + commands.offer(command); + } + + processCommands(); + } + static int[] tree; + static int[] candies; + static void processCommands() { + compress(); + tree = new int[4*size]; + candies = new int[size+1]; + while(!commands.isEmpty()) { + Command command = commands.poll(); + + if(command.cmd == IN) { + int candy = command.b; // 넣고자 하는 사탕의 맛 + int cnt = command.c; // 넣고자 하는 개수 + int id = valueToId.get(candy); + int cur = candies[id]; + int next = cur + cnt; + update(1, 1, size, id, id, next); + candies[id] = next; + } else if(command.cmd == OUT) { + int targetRank = command.b; // 찾고자하는 rank 값 + int id = query(1, 1, size, targetRank); // 해당 rank의 candy id + int cur = candies[id]; + int next = cur - 1; + update(1, 1, size, id, id, next); + candies[id] = next; + sb.append(idToValue.get(id)).append('\n'); + } + } + } + static int size; + static Map valueToId; + static Map idToValue; + static void compress() { + valueToId = new HashMap<>(); + idToValue = new HashMap<>(); + + int id = 1; + for(int i : values) { + valueToId.put(i, id); + idToValue.put(id, i); + id++; + } + + size = id-1; + } + static void update(int id, int l, int r, int s, int e, int v) { + if(r < s || l > e) return; + if(l >= s && r <= e) { + tree[id] = v; + return; + } + int mid = l + (r - l)/2; + update(2*id, l, mid, s, e, v); + update(2*id+1, mid+1, r, s, e, v); + tree[id] = tree[2*id] + tree[2*id+1]; + } + static int query(int id, int l, int r, int targetRank) { + if(l == r) return l; + + int mid = l + (r - l)/2; + if(tree[2*id] >= targetRank) return query(2*id, l, mid, targetRank); + else return query(2*id+1, mid+1, r, targetRank - tree[2*id]); + } +} diff --git "a/java/src/feb/week3/boj/\354\210\230\354\227\264\352\263\274\354\277\274\353\246\25437_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/boj/\354\210\230\354\227\264\352\263\274\354\277\274\353\246\25437_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..68427e6 --- /dev/null +++ "b/java/src/feb/week3/boj/\354\210\230\354\227\264\352\263\274\354\277\274\353\246\25437_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,113 @@ +package feb.week3.boj; + +import java.util.*; +import java.io.*; + +public class 수열과쿼리37_박재환 { + static BufferedReader br; + static StringBuilder sb; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + sb = new StringBuilder(); + init(); + br.close(); + System.out.println(sb); + } + static StringTokenizer st; + static void input() throws IOException { + n = Integer.parseInt(br.readLine().trim()); + arr = new int[n+1]; + st = new StringTokenizer(br.readLine().trim()); + for(int i=1; i 0) { + st = new StringTokenizer(br.readLine().trim()); + int cmd = Integer.parseInt(st.nextToken()); + if(cmd == 1) { + int targetId = Integer.parseInt(st.nextToken()); + int targetValue = Integer.parseInt(st.nextToken()); + tree.update(1, 1, n, targetId, targetId, targetValue); + } + else if(cmd == 2) { + int s = Integer.parseInt(st.nextToken()); + int e = Integer.parseInt(st.nextToken()); + sb.append(tree.evenQuery(1, 1, n, s, e)).append('\n'); + } + else if(cmd == 3) { + int s = Integer.parseInt(st.nextToken()); + int e = Integer.parseInt(st.nextToken()); + sb.append(tree.oddQuery(1, 1, n, s, e)).append('\n'); + } + } + } + static class SegmentTree { + /** + * 갱신 자체가 구간에 대한 업데이트는 아님 + */ + int size; + int[] even; + int[] odd; + + SegmentTree(int n) { + this.size = n; + this.even = new int[4*size]; + this.odd = new int[4*size]; + } + + void buildTree(int id, int l, int r) { + if(l == r) { // 리프노드라면 + // 현재 원소가 짝수인지 홀수인지 확인 후 갱신 + if(arr[l]%2 == 0) even[id]++; + else odd[id]++; + return; + } + int mid = l + (r-l)/2; + buildTree(2*id, l, mid); + buildTree(2*id+1, mid+1, r); + + // 홀수와 짝수 노드를 구분해서 갱신 + even[id] = even[2*id] + even[2*id+1]; + odd[id] = odd[2*id] + odd[2*id+1]; + } + + void update(int id, int l, int r, int s, int e, int v) { + if(r < s || l > e) return; + if(l >= s && r <= e) { // 리프노드 + // 기존 값을 확인한 뒤, 갱신 + int prev = arr[s]; + if(prev%2 == 0) even[id]--; + else odd[id]--; + // 새로운 값 갱신 + if(v%2 == 0) even[id]++; + else odd[id]++; + arr[l] = v; + return; + } + int mid = l + (r-l)/2; + update(2*id, l, mid, s, e, v); + update(2*id+1, mid+1, r, s, e, v); + // 홀수와 짝수 노드를 구분해서 갱신 + even[id] = even[2*id] + even[2*id+1]; + odd[id] = odd[2*id] + odd[2*id+1]; + } + int evenQuery(int id, int l, int r, int s, int e) { + if(r < s || l > e) return 0; + if(l >= s && r <= e) return even[id]; + int mid = l + (r-l)/2; + return evenQuery(2*id, l, mid, s, e) + evenQuery(2*id+1, mid+1, r, s, e); + } + int oddQuery(int id, int l, int r, int s, int e) { + if(r < s || l > e) return 0; + if(l >= s && r <= e) return odd[id]; + int mid = l + (r-l)/2; + return oddQuery(2*id, l, mid, s, e) + oddQuery(2*id+1, mid+1, r, s, e); + } + } +} diff --git "a/java/src/feb/week3/boj/\354\227\220\353\260\224\354\277\260_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/boj/\354\227\220\353\260\224\354\277\260_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..0b9994b --- /dev/null +++ "b/java/src/feb/week3/boj/\354\227\220\353\260\224\354\277\260_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,103 @@ +package feb.week3.boj; + +import java.util.*; +import java.io.*; + +public class 에바쿰_박재환 { + static BufferedReader br; + static StringBuilder sb; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + sb = new StringBuilder(); + init(); + br.close(); + System.out.println(sb); + } + static StringTokenizer st; + static int n; + static int[] arr; + static long[] tree; + static long[] lazy; + static void init() throws IOException { + st = new StringTokenizer(br.readLine().trim()); + n = Integer.parseInt(st.nextToken()); + int q1 = Integer.parseInt(st.nextToken()); + int q2 = Integer.parseInt(st.nextToken()); + + arr = new int[n+1]; + st = new StringTokenizer(br.readLine().trim()); + for(int i=1; i 0) { + st = new StringTokenizer(br.readLine().trim()); + int cmd = Integer.parseInt(st.nextToken()); + if(cmd == 1) { + int s = Integer.parseInt(st.nextToken()); + int e = Integer.parseInt(st.nextToken()); + if (s > e) { + int tmp = s; + s = e; + e = tmp; + } + sb.append(query(1, 1, n, s, e)).append('\n'); + } else if(cmd == 2) { + int s = Integer.parseInt(st.nextToken()); + int e = Integer.parseInt(st.nextToken()); + long v = Long.parseLong(st.nextToken()); + if (s > e) { + int tmp = s; + s = e; + e = tmp; + } + update(1, 1, n, s, e, v); + } + } + } + + static long buildTree(int id, int l, int r) { + if(l == r) return tree[id] = arr[l]; + int mid = l + (r - l)/2; + return tree[id] = buildTree(2*id, l, mid) + buildTree(2*id+1, mid+1, r); + } + + static void push(int id, int l, int r) { + if(lazy[id] == 0 || l == r) return; + + long lazyV = lazy[id]; + int mid = l + (r - l)/2; + + tree[2*id] += ((mid-l+1) * lazyV); + lazy[2*id] += lazyV; + + tree[2*id+1] += ((r-mid) * lazyV); + lazy[2*id+1] += lazyV; + + lazy[id] = 0; + } + + static long query(int id, int l, int r, int s, int e) { + if(r < s || l > e) return 0L; + if(l >= s && r <= e) return tree[id]; + push(id, l, r); + int mid = l + (r - l)/2; + return query(2*id, l, mid, s, e) + query(2*id+1, mid+1, r, s, e); + } + + static void update(int id, int l, int r, int s, int e, long v) { + if(r < s || l > e) return; + if(l >= s && r <= e) { + tree[id] += ((r - l + 1) * v); + lazy[id] += v; + return; + } + push(id, l, r); + int mid = l + (r - l)/2; + update(2*id, l, mid, s, e, v); + update(2*id+1, mid+1, r, s, e, v); + tree[id] = tree[2*id] + tree[2*id+1]; + } +} diff --git "a/java/src/feb/week3/boj/\354\230\244\353\246\204\354\204\270_\354\204\270\352\267\270\353\250\274\355\212\270\355\212\270\353\246\254_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/boj/\354\230\244\353\246\204\354\204\270_\354\204\270\352\267\270\353\250\274\355\212\270\355\212\270\353\246\254_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..9bf390c --- /dev/null +++ "b/java/src/feb/week3/boj/\354\230\244\353\246\204\354\204\270_\354\204\270\352\267\270\353\250\274\355\212\270\355\212\270\353\246\254_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,81 @@ +package feb.week3.boj; + +import java.util.*; +import java.io.*; + +public class 오름세_세그먼트트리_박재환 { + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StringTokenizer st; + String input; + while((input = br.readLine()) != null) { + int n = Integer.parseInt(input.trim()); + int[] arr = new int[n]; + st = new StringTokenizer(br.readLine().trim()); + for(int i=0; i map = new HashMap<>(); + for(int i : temp) { + if(!map.containsKey(i)) { + map.put(i, ++id); + } + } + + compressArr = new int[arr.length]; + size = id; + for(int i=0; i e) return 0; + if(l >= s && r <= e) return tree[id]; + + int mid = l + (r-l)/2; + return Math.max(query(2*id, l, mid, s, e), query(2*id+1, mid+1, r, s, e)); + } + static void update(int id, int l, int r, int s, int e, int v) { + if(r < s || l > e) return; + if(l >= s && r <= e) { + tree[id] = v; + return; + } + int mid = l + (r-l)/2; + update(2*id, l, mid, s, e, v); + update(2*id+1, mid+1, r, s, e, v); + tree[id] = Math.max(tree[2*id], tree[2*id+1]); + } +} diff --git "a/java/src/feb/week3/boj/\354\230\244\353\246\204\354\204\270_\354\235\264\353\266\204\355\203\220\354\203\211_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/boj/\354\230\244\353\246\204\354\204\270_\354\235\264\353\266\204\355\203\220\354\203\211_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..89ba3c9 --- /dev/null +++ "b/java/src/feb/week3/boj/\354\230\244\353\246\204\354\204\270_\354\235\264\353\266\204\355\203\220\354\203\211_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,50 @@ +package feb.week3.boj; + +import java.util.*; +import java.io.*; + +public class 오름세_이분탐색_박재환 { + static BufferedReader br; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + init(); + br.close(); + } + static StringTokenizer st; + static void init() throws IOException { + String input; + while((input = br.readLine()) != null) { + if(input.isEmpty()) break; + int n = Integer.parseInt(input.trim()); + int[] arr = new int[n]; + st = new StringTokenizer(br.readLine().trim()); + for(int i=0; i lisList; + static int getListByBinarySearch(int n, int[] arr) { + lisList = new ArrayList<>(); + for(int i : arr) { + int insertIdx = lisList.isEmpty() ? 0 : findInsertIdx(i); + if(insertIdx == lisList.size()) lisList.add(i); + else lisList.set(insertIdx, i); + } + return lisList.size(); + } + + /** + * [Lower Bound] + * target 보다 큰 값중 가장 작은 값 + */ + static int findInsertIdx(int target) { + int l = 0, r = lisList.size(); + while(l < r) { + int mid = l + (r-l)/2; + if(target <= lisList.get(mid)) r = mid; + else l = mid+1; + } + return l; + } +} diff --git "a/java/src/feb/week3/boj/\354\232\251\354\225\241_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/boj/\354\232\251\354\225\241_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..be2b02e --- /dev/null +++ "b/java/src/feb/week3/boj/\354\232\251\354\225\241_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,61 @@ +package feb.week3.boj; + +import java.util.*; +import java.io.*; + +public class 용액_박재환 { + static BufferedReader br; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + init(); + br.close(); + } + static StringTokenizer st; + static int n; + static int[] arr; + static void init() throws IOException { + n = Integer.parseInt(br.readLine().trim()); + arr = new int[n]; + st = new StringTokenizer(br.readLine().trim()); + for(int i=0; i 큰 값] + * + * [투포인터] + * l = 0, r = n-1 + * arr[l] + arr[r] == 0 -> 최적값 (탐색할 필요 X) + * arr[l] + arr[r] < 0 -> 음수 (작은 값을 좀 더 올려야함 -> l++) + * arr[l] + arr[r] > 0 -> 양수 (큰 값을 좀 더 내려야함 -> r--) + */ + int l = 0, r = n-1; + int sum = Integer.MAX_VALUE; + int lBest = -1, rBest = -1; + while(l < r) { // l 과 r 이 같아질 수 없음 + if(arr[l] + arr[r] == 0) { + sum = 0; + lBest = arr[l]; + rBest = arr[r]; + break; + } + + int tempSum = arr[l] + arr[r]; + if(tempSum > 0) { + if(Math.abs(tempSum) < Math.abs(sum)) { + sum = tempSum; + lBest = arr[l]; + rBest = arr[r]; + } + r--; + }else { + if(Math.abs(tempSum) < Math.abs(sum)) { + sum = tempSum; + lBest = arr[l]; + rBest = arr[r]; + } + l++; + } + } + System.out.println(lBest + " " + rBest); + } +} diff --git "a/java/src/feb/week3/boj/\355\203\200\354\236\204\353\250\270\354\213\240_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/boj/\355\203\200\354\236\204\353\250\270\354\213\240_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..6b09fdc --- /dev/null +++ "b/java/src/feb/week3/boj/\355\203\200\354\236\204\353\250\270\354\213\240_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,74 @@ +package feb.week3.boj; + +import java.util.*; +import java.io.*; + +public class 타임머신_박재환 { + static BufferedReader br; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + init(); + br.close(); + } + static StringTokenizer st; + static int n; + static List[] connections; + static void init() throws IOException { + st = new StringTokenizer(br.readLine().trim()); + n = Integer.parseInt(st.nextToken()); + int m = Integer.parseInt(st.nextToken()); + + connections = new List[n+1]; + for(int i=0; i(); + + while(m-- > 0) { + st = new StringTokenizer(br.readLine().trim()); + int from = Integer.parseInt(st.nextToken()); + int to = Integer.parseInt(st.nextToken()); + int cost = Integer.parseInt(st.nextToken()); + connections[from].add(new int[] {to, cost}); + } + + // 출발 위치 + long[] dist = new long[n+1]; + Arrays.fill(dist, Long.MAX_VALUE); + dist[1] = 0; + + for(int i=1; i 모든 노드가 연결되어있다고 가정했을 때 최대 n-1개의 간선 존재 + for(int from=1; from cost + dist[from]) { + dist[to] = cost + dist[from]; + } + } + } + } + + for(int from=1; from 사이클 존재 + if(dist[from] == Long.MAX_VALUE) continue; + + for(int[] connection : connections[from]) { + int to = connection[0]; + int cost = connection[1]; + + if(dist[to] > cost + dist[from]) { + System.out.println(-1); + return; + } + } + } + StringBuilder sb = new StringBuilder(); + for(int i=2; i 메두사가 전사 공격 -> 전사 사라짐 + * - 여러 최단 경로가 존재하는 경우 [상 하 좌 우] 우선 순위를 가짐 + * - 집에서 공원으로 도달할 수 있는 경우도 있음 + * + * 2. 메두사 시선 + * - [상 하 좌 우] 하나의 방향을 선택해 바라봄 + * - 바라보는 방향으로 90도의 시야각을 갖는다. + * - 다른 전사에게 가려진 전사는 보이지 않는다. + * - 돌로 변한 전사는 이번 턴에는 움직이지 못하고, 다음 턴에 움직일 수 있다. + * - 전사를 가장 많이 볼 수 있는 방향을 바라본다. [상 하 좌 우] 우선 순위를 갖는다. + * + * 3. 전사들의 이동 + * - 메두사를 향해 최대 두 칸까지 이동한다. + * - 이동 중 전사들은 같은 칸을 공유할 수 있다. + * 3-1. 첫 번째 이동 + * - 메두사와 거리를 줄일 수 있는 방향으로 한 칸 이동한다. [상 하 좌 우] 우선순위 + * - 메두사의 시야가 들어오는 곳으로 이동 불가 + * 3-2. 두 번째 이동 + * - 메두사와 거리를 줄일 수 있는 방향으로 한 칸 이동한다. [좌 우 상 하] 우선순위 + * - 메두사의 시야가 들어오는 곳으로 이동 불가 + * + * 4. 전사의 공격 + * - 메두사를 공격하고 사라진다. + */ + static BufferedReader br; + static StringBuilder sb; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + sb = new StringBuilder(); + init(); + br.close(); + System.out.println(sb); + } + static class Monster { + int x, y; + + Monster(int x, int y) { + this.x = x; + this.y = y; + } + } + static class Park { + int x, y; + + Park(int x, int y) { + this.x = x; + this.y = y; + } + } + static class Worrier { + int x, y; + boolean stone; + boolean live; + + Worrier(int x, int y) { + this.x = x; + this.y = y; + this.stone = false; + this.live = true; + } + } + static void allocateMonster() { + int x = Integer.parseInt(st.nextToken()); + int y = Integer.parseInt(st.nextToken()); + monster = new Monster(x, y); + } + static void allcatePark() { + int x = Integer.parseInt(st.nextToken()); + int y = Integer.parseInt(st.nextToken()); + park = new Park(x, y); + } + static void allocateWorrier() { + worriers = new Worrier[m+1]; + for(int i=1; i 첫 위치는 상관 X + shortestPathToPark.pollLast(); // 공원에 도착하는 위치 제거 + while(!shortestPathToPark.isEmpty()) { + int[] monsterLocation = shortestPathToPark.pollFirst(); // 현재 메두사의 위치 + // 메두사 위치 업데이트 + monster.x = monsterLocation[0]; + monster.y = monsterLocation[1]; + /** + * [메두사 시선] + */ + int stoneWorrier = monsterEyeSight(monsterLocation[0], monsterLocation[1]); + /** + * 전사들이 이동한다. + */ + int[] worrierInfo = moveWorrier(); + // 전사들의 모든 이동거리 / 돌이 된 전사 수 / 공격에 성공한 전사 수 + sb.append(worrierInfo[0]).append(' ').append(stoneWorrier).append(' ').append(worrierInfo[1]).append('\n'); + resetWorrier(); + } + // 공원에 도칙 + sb.append(0); + } + /** + * [메두사의 이동] + */ + static class Node { + int x, y; + Node prev; + + Node(int x, int y, Node prev) { + this.x = x; + this.y = y; + this.prev = prev; + } + } + static Deque shortestPathToPark; + static boolean moveMonster() { + // 메두사의 이동 [상 하 좌 우] 우선순위 + int[] mDx = {-1,1,0,0}; + int[] mDy = {0,0,-1,1}; + /** + * 메두사가 공원까지 이동하는 최단 경로를 구할 수 있다. + * + * 1회 수행으로 루트를 미리 구해둔다. + */ + int x = monster.x, y = monster.y; + Queue q = new ArrayDeque<>(); + boolean[][] visited = new boolean[n][n]; + + q.offer(new Node(x, y, null)); + visited[x][y] = true; + + while(!q.isEmpty()) { + Node cur = q.poll(); + + if(cur.x == park.x && cur.y == park.y) { // 공원에 도착 + restorePathToPark(cur); + return true; + } + + for(int dir=0; dir<4; dir++) { + int nx = cur.x + mDx[dir]; + int ny = cur.y + mDy[dir]; + + if(isNotBoard(nx, ny) || visited[nx][ny]) continue; + if(board[nx][ny] == 1) continue; + + Node next = new Node(nx, ny, cur); + visited[nx][ny] = true; + q.offer(next); + } + } + + return false; // 공원까지 이동 가능한 경로가 존재하지 않음 + } + static void restorePathToPark(Node cur) { + /** + * 메두사가 공원까지 이동하는 최단 경로를 복원한다. + */ + shortestPathToPark = new ArrayDeque<>(); + + while(cur != null) { + shortestPathToPark.offerFirst(new int[] {cur.x, cur.y}); + cur = cur.prev; + } + } + static boolean[][] cantGo; + static int monsterEyeSight(int x, int y) { + /** + * 메두사 시선 + * + * 각 위치별로 볼 수 있는 전사 수를 구한다. + * - 시선의 범위 + * - 가려지는 전사 + */ + // 메두사의 시선 [상 하 좌 우] 우선순위 + int[] mDx = {-1,1,0,0}; + int[] mDy = {0,0,-1,1}; + List[][] worrierBoard = getWorrierBoard(); // 전사들의 현 위치 + + // 현 위치에 전사가 있다면, 해당 전사는 사라짐 + for(int id : worrierBoard[x][y]) worriers[id].live = false; + + // 각 방향별로 메두사가 볼 수 있는 전사의 수를 기록 + List findWorrier = new ArrayList<>(); + for(int dir=0; dir<4; dir++) { + boolean[][] eyeSight = new boolean[n][n]; // 메두사의 시야 + int nx = x + mDx[dir], ny = y + mDy[dir]; + + int depth = 1; + while(!isNotBoard(nx, ny)) { // 격자를 벗어나지 않을때까지만 + // 1. 바라보는 방향으로 일직선 + eyeSight[nx][ny] = true; + // 2. 일직선을 기준으로 좌/우 전파 [0, 1] -> [2, 3] / [2, 3] -> [0, 1] + if(dir == 0 || dir == 1) { // 좌 우 + for(int i=1; i worrierList = new ArrayList<>(); + boolean[] isFound = new boolean[m+1]; + depth = 1; + while(!isNotBoard(nx, ny)) { // 격자를 벗어나지 않을때까지만 + // 1. 바라보는 방향으로 일직선 + if(eyeSight[nx][ny] && !worrierBoard[nx][ny].isEmpty()) { + /** + * 일직선 뒤로는 더 이상 적을 보지 못함 + */ + addWorriersToSight(worrierBoard[nx][ny], worrierList, isFound); + for(int i=1; i [2, 3] / [2, 3] -> [0, 1] + if(dir == 0 || dir == 1) { // 좌 우 + for(int i=1; i[][] getWorrierBoard() { + List[][] worrierBoard = new ArrayList[n][n]; + for(int x=0; x(); + } + for(int i=1; i ids, List worrierList, boolean[] isFound) { + for(int id : ids) { + if(isFound[id]) continue; + worrierList.add(id); + isFound[id] = true; + } + } + static int[] moveWorrier() { + int attackCount = 0; + int moveCount = 0; + for(Worrier w : worriers) { + if(w == null || !w.live || w.stone) continue; // 사라졌거나, 돌이 되었다면 이동할 수 없다. + if(moveFirst(w)) { + moveCount++; + if (monster.x == w.x && monster.y == w.y) { // 메두사와 만난 경우 + attackCount++; + w.live = false; + continue; + } + } + if(moveSecond(w)) { + moveCount++; + if (monster.x == w.x && monster.y == w.y) { // 메두사와 만난 경우 + attackCount++; + w.live = false; + } + } + } + return new int[] {moveCount, attackCount}; + } + static boolean moveFirst(Worrier w) { + int[] dx = {-1,1,0,0}; + int[] dy = {0,0,-1,1}; + int distFrom = Math.abs(w.x - monster.x) + Math.abs(w.y - monster.y); // 멘헤튼 거리 기준 + for(int dir=0; dir<4; dir++) { + int nx = w.x + dx[dir]; + int ny = w.y + dy[dir]; + + if(isNotBoard(nx, ny) || cantGo[nx][ny]) continue; // 격자를 벗어나지 않고, 메두사의 시야에 들어가지 않는 경우 + int tempDist = Math.abs(nx - monster.x) + Math.abs(ny - monster.y); // 멘헤튼 거리 기준 + if(tempDist < distFrom) { + w.x = nx; + w.y = ny; + return true; + } + } + return false; + } + static boolean moveSecond(Worrier w) { + int[] dx = {0,0,-1,1}; + int[] dy = {-1,1,0,0}; + int distFrom = Math.abs(w.x - monster.x) + Math.abs(w.y - monster.y); // 멘헤튼 거리 기준 + for(int dir=0; dir<4; dir++) { + int nx = w.x + dx[dir]; + int ny = w.y + dy[dir]; + + if(isNotBoard(nx, ny) || cantGo[nx][ny]) continue; // 격자를 벗어나지 않고, 메두사의 시야에 들어가지 않는 경우 + int tempDist = Math.abs(nx - monster.x) + Math.abs(ny - monster.y); // 멘헤튼 거리 기준 + if(tempDist < distFrom) { + w.x = nx; + w.y = ny; + return true; + } + } + return false; + } + static void resetWorrier() { + for(Worrier w : worriers) { + if(w == null || !w.live) continue; + w.stone = false; + } + } + //---------------------------------------------------------------------- + static boolean isNotBoard(int x, int y) { + return x < 0 || y < 0 || x+1 > n || y+1 > n; + } +} diff --git "a/java/src/feb/week3/codetree/\353\251\224\353\221\220\354\202\254\354\231\200\354\240\204\354\202\254\353\223\244_\354\265\234\354\240\201\355\231\224_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/codetree/\353\251\224\353\221\220\354\202\254\354\231\200\354\240\204\354\202\254\353\223\244_\354\265\234\354\240\201\355\231\224_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..29148ba --- /dev/null +++ "b/java/src/feb/week3/codetree/\353\251\224\353\221\220\354\202\254\354\231\200\354\240\204\354\202\254\353\223\244_\354\265\234\354\240\201\355\231\224_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,445 @@ +package feb.week3.codetree; + +import java.io.*; +import java.util.*; + +public class 메두사와전사들_최적화_박재환 { + static BufferedReader br; + static StringTokenizer st; + static StringBuilder sb; + + static int n, m; + static int[][] board; + + static int medusaX, medusaY; + static int parkX, parkY; + + static int[] wx, wy; + static boolean[] alive, stone; + + static int[][] head; + static int[] next; + + static boolean[][] cantGo; + + static final int[] UDLR_X = {-1, 1, 0, 0}; + static final int[] UDLR_Y = {0, 0, -1, 1}; + static final int[] LRUD_X = {0, 0, -1, 1}; + static final int[] LRUD_Y = {-1, 1, 0, 0}; + + // 한 방향 시선 평가 결과: + // - count: 이 방향에서 돌로 만드는 전사 수 + // - canSee: 이 방향 시야(전사 이동 금지 영역으로 재사용) + // - ids/size: 돌이 될 전사 ID 목록 + static final class GazeResult { + int count; + boolean[][] canSee; + int[] ids; + int size; + + GazeResult(int count, boolean[][] canSee, int[] ids, int size) { + this.count = count; + this.canSee = canSee; + this.ids = ids; + this.size = size; + } + } + + static final class IntCollector { + int[] arr = new int[16]; + int size = 0; + + void add(int v) { + if (size == arr.length) arr = Arrays.copyOf(arr, arr.length << 1); + arr[size++] = v; + } + } + + public static void main(String[] args) throws Exception { + br = new BufferedReader(new InputStreamReader(System.in)); + sb = new StringBuilder(); + + input(); + List path = shortestPath(); + if (path == null) { + System.out.println(-1); + return; + } + + simulate(path); + System.out.print(sb); + } + + static void input() throws Exception { + st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); + m = Integer.parseInt(st.nextToken()); + + st = new StringTokenizer(br.readLine()); + medusaX = Integer.parseInt(st.nextToken()); + medusaY = Integer.parseInt(st.nextToken()); + parkX = Integer.parseInt(st.nextToken()); + parkY = Integer.parseInt(st.nextToken()); + + wx = new int[m]; + wy = new int[m]; + alive = new boolean[m]; + stone = new boolean[m]; + Arrays.fill(alive, true); + + st = new StringTokenizer(br.readLine()); + for (int i = 0; i < m; i++) { + wx[i] = Integer.parseInt(st.nextToken()); + wy[i] = Integer.parseInt(st.nextToken()); + } + + board = new int[n][n]; + for (int i = 0; i < n; i++) { + st = new StringTokenizer(br.readLine()); + for (int j = 0; j < n; j++) { + board[i][j] = Integer.parseInt(st.nextToken()); + } + } + + head = new int[n][n]; + next = new int[m]; + cantGo = new boolean[n][n]; + } + + // 메두사 최단 경로를 한 번만 계산한다. + // BFS 확장 순서를 상/하/좌/우로 고정해 동일 거리에서 우선순위를 보장한다. + static List shortestPath() { + boolean[][] vis = new boolean[n][n]; + int[][] px = new int[n][n]; + int[][] py = new int[n][n]; + for (int i = 0; i < n; i++) { + Arrays.fill(px[i], -1); + Arrays.fill(py[i], -1); + } + + ArrayDeque q = new ArrayDeque<>(); + q.offer(new int[]{medusaX, medusaY}); + vis[medusaX][medusaY] = true; + + while (!q.isEmpty()) { + int[] cur = q.poll(); + int x = cur[0], y = cur[1]; + if (x == parkX && y == parkY) break; + + for (int d = 0; d < 4; d++) { + int nx = x + UDLR_X[d]; + int ny = y + UDLR_Y[d]; + if (!inRange(nx, ny) || vis[nx][ny] || board[nx][ny] == 1) continue; + vis[nx][ny] = true; + px[nx][ny] = x; + py[nx][ny] = y; + q.offer(new int[]{nx, ny}); + } + } + + if (!vis[parkX][parkY]) return null; + + ArrayDeque rev = new ArrayDeque<>(); + int cx = parkX, cy = parkY; + while (cx != -1) { + rev.addLast(new int[]{cx, cy}); + int tx = px[cx][cy]; + int ty = py[cx][cy]; + cx = tx; + cy = ty; + } + + List path = new ArrayList<>(rev.size()); + while (!rev.isEmpty()) path.add(rev.pollLast()); + return path; + } + + static void simulate(List path) { + // 시작점(path[0]) 제외, 공원(path[last]) 도착 턴은 출력 0 후 종료 + // 매 턴 순서: + // 1) 메두사 이동 2) 같은 칸 전사 제거 3) 최적 시선 선택/석화 + // 4) 전사 1차+2차 이동 5) 턴 결과 기록 + for (int i = 1; i < path.size() - 1; i++) { + medusaX = path.get(i)[0]; + medusaY = path.get(i)[1]; + + killWarriorsOnMedusa(); + buildOccupancy(); + + GazeResult best = chooseBestGaze(); + for (int k = 0; k < best.size; k++) stone[best.ids[k]] = true; + cantGo = best.canSee; + + int[] moveInfo = moveWarriors(); + sb.append(moveInfo[0]).append(' ') + .append(best.count).append(' ') + .append(moveInfo[1]).append('\n'); + + resetStone(); + } + sb.append(0); + } + + static void killWarriorsOnMedusa() { + for (int i = 0; i < m; i++) { + if (alive[i] && wx[i] == medusaX && wy[i] == medusaY) alive[i] = false; + } + } + + static void buildOccupancy() { + // 칸당 다수 전사를 O(1) 삽입 가능한 단일 연결리스트로 관리: + // head[x][y] -> 전사 인덱스 체인(next) + for (int i = 0; i < n; i++) Arrays.fill(head[i], -1); + Arrays.fill(next, -1); + for (int i = 0; i < m; i++) { + if (!alive[i]) continue; + int x = wx[i], y = wy[i]; + next[i] = head[x][y]; + head[x][y] = i; + } + } + + static GazeResult chooseBestGaze() { + // 동률이면 먼저 평가한 방향 유지: 상(0), 하(1), 좌(2), 우(3) + GazeResult best = null; + for (int dir = 0; dir < 4; dir++) { + GazeResult cur = evalDirection(dir); + if (best == null || cur.count > best.count) best = cur; + } + return best; + } + + static GazeResult evalDirection(int dir) { + boolean[][] canSee = new boolean[n][n]; + // 먼저 90도 시야(삼각형)를 채우고, 이후 전사를 만나며 가림(그림자) 처리 + markTriangle(canSee, dir); + + IntCollector ids = new IntCollector(); + int cnt = 0; + + if (dir == 0 || dir == 1) { + int step = (dir == 0) ? -1 : 1; + for (int depth = 1; depth < n; depth++) { + int x = medusaX + step * depth; + if (!inRange(x, medusaY)) break; + + // 메두사와 가까운 depth부터 탐색해야 가림 규칙이 정확해진다. + cnt += processCellAndShadow(canSee, ids, dir, x, medusaY); + + for (int off = 1; off <= depth; off++) { + int yR = medusaY + off; + if (inRange(x, yR)) cnt += processCellAndShadow(canSee, ids, dir, x, yR); + int yL = medusaY - off; + if (inRange(x, yL)) cnt += processCellAndShadow(canSee, ids, dir, x, yL); + } + } + } else { + int step = (dir == 2) ? -1 : 1; + for (int depth = 1; depth < n; depth++) { + int y = medusaY + step * depth; + if (!inRange(medusaX, y)) break; + + cnt += processCellAndShadow(canSee, ids, dir, medusaX, y); + + for (int off = 1; off <= depth; off++) { + int xD = medusaX + off; + if (inRange(xD, y)) cnt += processCellAndShadow(canSee, ids, dir, xD, y); + int xU = medusaX - off; + if (inRange(xU, y)) cnt += processCellAndShadow(canSee, ids, dir, xU, y); + } + } + } + + return new GazeResult(cnt, canSee, ids.arr, ids.size); + } + + static int processCellAndShadow(boolean[][] canSee, IntCollector ids, int dir, int x, int y) { + if (!canSee[x][y] || head[x][y] == -1) return 0; + + int add = 0; + // 같은 칸의 전사는 전부 함께 석화 대상으로 들어간다. + for (int p = head[x][y]; p != -1; p = next[p]) { + ids.add(p); + add++; + } + + // 이 칸 전사로 인해 뒤쪽 시야를 차단한다. + applyShadow(canSee, dir, x, y); + return add; + } + + static void applyShadow(boolean[][] canSee, int dir, int x, int y) { + // 가림 처리 핵심: + // - 중심선(메두사와 같은 행/열) 전사는 뒤 직선 시야를 모두 가린다. + // - 좌/우(또는 상/하)로 치우친 전사는 뒤쪽으로 부채꼴 그림자를 만든다. + if (dir == 0 || dir == 1) { + int stepX = (dir == 0) ? -1 : 1; + int side = y - medusaY; + + if (side == 0) { + for (int nx = x + stepX; inRange(nx, y); nx += stepX) { + canSee[nx][y] = false; + } + return; + } + + int nx = x + stepX; + int depth = 1; + if (side > 0) { + while (inRange(nx, y)) { + for (int k = 0; k <= depth; k++) { + int ny = y + k; + if (!inRange(nx, ny)) break; + canSee[nx][ny] = false; + } + nx += stepX; + depth++; + } + } else { + while (inRange(nx, y)) { + for (int k = 0; k <= depth; k++) { + int ny = y - k; + if (!inRange(nx, ny)) break; + canSee[nx][ny] = false; + } + nx += stepX; + depth++; + } + } + } else { + int stepY = (dir == 2) ? -1 : 1; + int side = x - medusaX; + + if (side == 0) { + for (int ny = y + stepY; inRange(x, ny); ny += stepY) { + canSee[x][ny] = false; + } + return; + } + + int ny = y + stepY; + int depth = 1; + if (side > 0) { + while (inRange(x, ny)) { + for (int k = 0; k <= depth; k++) { + int nx = x + k; + if (!inRange(nx, ny)) break; + canSee[nx][ny] = false; + } + ny += stepY; + depth++; + } + } else { + while (inRange(x, ny)) { + for (int k = 0; k <= depth; k++) { + int nx = x - k; + if (!inRange(nx, ny)) break; + canSee[nx][ny] = false; + } + ny += stepY; + depth++; + } + } + } + } + + static void markTriangle(boolean[][] canSee, int dir) { + // 방향별 90도 시야(맨해튼 깊이 d에서 폭 2d+1)를 미리 마킹 + if (dir == 0) { + for (int d = 1; d < n; d++) { + int x = medusaX - d; + if (x < 0) break; + int l = Math.max(0, medusaY - d); + int r = Math.min(n - 1, medusaY + d); + for (int y = l; y <= r; y++) canSee[x][y] = true; + } + } else if (dir == 1) { + for (int d = 1; d < n; d++) { + int x = medusaX + d; + if (x >= n) break; + int l = Math.max(0, medusaY - d); + int r = Math.min(n - 1, medusaY + d); + for (int y = l; y <= r; y++) canSee[x][y] = true; + } + } else if (dir == 2) { + for (int d = 1; d < n; d++) { + int y = medusaY - d; + if (y < 0) break; + int u = Math.max(0, medusaX - d); + int b = Math.min(n - 1, medusaX + d); + for (int x = u; x <= b; x++) canSee[x][y] = true; + } + } else { + for (int d = 1; d < n; d++) { + int y = medusaY + d; + if (y >= n) break; + int u = Math.max(0, medusaX - d); + int b = Math.min(n - 1, medusaX + d); + for (int x = u; x <= b; x++) canSee[x][y] = true; + } + } + } + + static int[] moveWarriors() { + int moveCount = 0; + int attackCount = 0; + + for (int i = 0; i < m; i++) { + if (!alive[i] || stone[i]) continue; + + // 1차 이동 우선순위: 상/하/좌/우 + if (moveOneStep(i, UDLR_X, UDLR_Y)) { + moveCount++; + if (wx[i] == medusaX && wy[i] == medusaY) { + alive[i] = false; + attackCount++; + continue; + } + } + + // 2차 이동 우선순위: 좌/우/상/하 + if (moveOneStep(i, LRUD_X, LRUD_Y)) { + moveCount++; + if (wx[i] == medusaX && wy[i] == medusaY) { + alive[i] = false; + attackCount++; + } + } + } + + return new int[]{moveCount, attackCount}; + } + + static boolean moveOneStep(int id, int[] dx, int[] dy) { + int curDist = dist(wx[id], wy[id], medusaX, medusaY); + + for (int d = 0; d < 4; d++) { + int nx = wx[id] + dx[d]; + int ny = wy[id] + dy[d]; + // 시야(cantGo)로는 이동할 수 없고, 메두사와 거리가 엄격히 감소해야만 이동 + if (!inRange(nx, ny) || cantGo[nx][ny]) continue; + + int nd = dist(nx, ny, medusaX, medusaY); + if (nd < curDist) { + wx[id] = nx; + wy[id] = ny; + return true; + } + } + return false; + } + + static void resetStone() { + for (int i = 0; i < m; i++) { + if (alive[i]) stone[i] = false; + } + } + + static int dist(int x1, int y1, int x2, int y2) { + return Math.abs(x1 - x2) + Math.abs(y1 - y2); + } + + static boolean inRange(int x, int y) { + return x >= 0 && y >= 0 && x < n && y < n; + } +} diff --git "a/java/src/feb/week3/codetree/\353\257\270\354\247\200\354\235\230\352\263\265\352\260\204\355\203\210\354\266\234_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/codetree/\353\257\270\354\247\200\354\235\230\352\263\265\352\260\204\355\203\210\354\266\234_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..05240f6 --- /dev/null +++ "b/java/src/feb/week3/codetree/\353\257\270\354\247\200\354\235\230\352\263\265\352\260\204\355\203\210\354\266\234_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,440 @@ +package feb.week3.codetree; + +import java.util.*; +import java.io.*; + +public class 미지의공간탈출_박재환 { + static BufferedReader br; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + init(); + br.close(); + } + + /** + * N x N 격지 내에 한 변의 길이가 M인 정육면체 형태의 시간의 벽이 세워져 있다. + * + * [타임머신 스캔] + * - 미지의 공간의 평면도 : 위에서 내려다본 전체 맵 + * - 시간의 벽의 단면도 : 시간의 벽의 윗 변과 동서남북 네 면의 단면도 + * + * 각 칸은 빈 공간(0)과 장애물(1)로 구성 + * + * 타임머신은 시간의 벽의 윗면 어딘가 위치, 2로 표시된다. + * 미지의 공간의 평면도에는 시간의 벽의 위치 3, 탈출구 4가 주어진다. + * + * 미지의 공간의 바닥에는 F개의 시간이상현상이 존재 + * - 매 v[i]의 배수 턴마다 방향 d[i]로 한 칸씩 확산된다. + * + * 오른쪽 0 + * 왼쪽 1 + * 아래쪽 2 + * 위쪽 3 + */ + static final int[] dx = {0, 0, 1, -1}; + static final int[] dy = {1, -1, 0, 0}; + + static StringTokenizer st; + static int n, m, f; + static int[][] miziBoard; + static int[][][] siganBoard; + static int[][] strangeTime; + static int siganTime; + static void init() throws IOException { + st = new StringTokenizer(br.readLine().trim()); + n = Integer.parseInt(st.nextToken()); + m = Integer.parseInt(st.nextToken()); + f = Integer.parseInt(st.nextToken()); + + miziBoard = new int[n][n]; + for (int x = 0; x < n; x++) { + st = new StringTokenizer(br.readLine().trim()); + for (int y = 0; y < n; y++) miziBoard[x][y] = Integer.parseInt(st.nextToken()); + } + + siganBoard = new int[5][m][m]; // 동 서 남 북 윗면 + for (int dir = 0; dir < 5; dir++) { + for (int x = 0; x < m; x++) { + st = new StringTokenizer(br.readLine().trim()); + for (int y = 0; y < m; y++) siganBoard[dir][x][y] = Integer.parseInt(st.nextToken()); + } + } + strangeTime = new int[f][4]; + for(int i=0; i q = new ArrayDeque<>(); + boolean[][][] visited = new boolean[5][m][m]; + + q.offer(new int[]{TOP, start[0], start[1], 0}); + visited[TOP][start[0]][start[1]] = true; + + while (!q.isEmpty()) { + int[] cur = q.poll(); + int face = cur[0]; + int x = cur[1]; + int y = cur[2]; + int time = cur[3]; + if(exitFace == face && exitX == x && exitY == y) return time; + for (int dir = 0; dir < 4; dir++) { + int nx = x + dx[dir]; + int ny = y + dy[dir]; + + if (nx < 0 || ny < 0 || nx >= m || ny >= m) { + /** + * 면을 이동하는 경우 + * + * 현재 면을 기준으로 하드코딩 + */ + if (face == TOP) { + if (nx < 0) { // NORTH + int nFace = NORTH; + int nX = 0; + int nY = m - 1 - y; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } else if (nx >= m) { // SOUTH + int nFace = SOUTH; + int nX = 0; + int nY = ny; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } else if (ny < 0) { // WEST + int nFace = WEST; + int nX = 0; + int nY = x; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } else if (ny >= m) { // EAST + int nFace = EAST; + int nX = 0; + int nY = m - 1 - x; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } + } else if (face == EAST) { + if (nx < 0) { // TOP + int nFace = TOP; + int nX = m - 1 - y; + int nY = m - 1; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } else if (nx >= m) { // mizi -> 처리 X + } else if (ny < 0) { // SOUTH + int nFace = SOUTH; + int nX = x; + int nY = m - 1; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } else if (ny >= m) { // NORTH + int nFace = NORTH; + int nX = nx; + int nY = 0; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } + } else if (face == WEST) { + if (nx < 0) { // TOP + int nFace = TOP; + int nX = ny; + int nY = 0; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } else if (nx >= m) { // mizi -> 처리 X + } else if (ny < 0) { // NORTH + int nFace = NORTH; + int nX = nx; + int nY = m-1; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } else if (ny >= m) { // SOUTH + int nFace = SOUTH; + int nX = nx; + int nY = 0; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } + } else if (face == SOUTH) { + if (nx < 0) { // TOP + int nFace = TOP; + int nX = m - 1; + int nY = y; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } else if (nx >= m) { // mizi -> 처리 X + } else if (ny < 0) { // WEST + int nFace = WEST; + int nX = nx; + int nY = m-1 ; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } else if (ny >= m) { // EAST + int nFace = EAST; + int nX = nx; + int nY = 0; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } + } else if (face == NORTH) { + if (nx < 0) { // TOP + int nFace = TOP; + int nX = 0; + int nY = m - 1 - y; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } else if (nx >= m) { // mizi -> 처리 X + } else if (ny < 0) { // EAST + int nFace = EAST; + int nX = nx; + int nY = m - 1; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } else if (ny >= m) { // WEST + int nFace = WEST; + int nX = nx; + int nY = 0; + + if (!visited[nFace][nX][nY] && siganBoard[nFace][nX][nY] == 0) { + visited[nFace][nX][nY] = true; + q.offer(new int[]{nFace, nX, nY, time+1}); + } + } + } + continue; + } + if (visited[face][nx][ny] || siganBoard[face][nx][ny] == 1) continue; + visited[face][nx][ny] = true; + q.offer(new int[]{face, nx, ny, time+1}); + } + } + return -1; + } + + static final int TOP = 4; + static final int NORTH = 3; + static final int SOUTH = 2; + static final int WEST = 1; + static final int EAST = 0; + static int[] findSiganStartXY() { + for (int x = 0; x < m; x++) { + for (int y = 0; y < m; y++) { + if (siganBoard[4][x][y] == 2) return new int[]{x, y}; + } + } + return null; + } + + static int[] entryMizi() { + /** + * 평면에서 정육면체가 차지하는 영역 + */ + int minX = n + 1, minY = n + 1, maxX = -1, maxY = -1; + for (int x = 0; x < n; x++) { + for (int y = 0; y < n; y++) { + if (miziBoard[x][y] == 3) { + minX = Math.min(x, minX); + maxX = Math.max(x, maxX); + minY = Math.min(y, minY); + maxY = Math.max(y, maxY); + } + } + } + + int entryX = -1; + int entryY = -1; + int entryDir = -1; + int offset = -1; + for (int x = minX; x <= maxX; x++) { + // WEST + if (minY - 1 >= 0 && miziBoard[x][minY - 1] == 0) { + entryX = x; + entryY = minY - 1; + entryDir = WEST; + offset = x - minX; + return new int[]{entryX, entryY, entryDir, offset}; + } + // EAST + if (maxY + 1 < n && miziBoard[x][maxY + 1] == 0) { + entryX = x; + entryY = maxY + 1; + entryDir = EAST; + offset = (m - 1) - (x - minX); + return new int[]{entryX, entryY, entryDir, offset}; + } + } + + for (int y = minY; y <= maxY; y++) { + // NORTH + if (minX - 1 >= 0 && miziBoard[minX - 1][y] == 0) { + entryX = minX - 1; + entryY = y; + entryDir = NORTH; + offset = (m - 1) - (y - minY); + return new int[]{entryX, entryY, entryDir, offset}; + } + // SOUTH + if (maxX + 1 < n && miziBoard[maxX + 1][y] == 0) { + entryX = maxX + 1; + entryY = y; + entryDir = SOUTH; + offset = y - minY; + return new int[]{entryX, entryY, entryDir, offset}; + } + } + + return null; + } + static int findMintimeToExit(int time) { + /** + * 출구 위치 찾기 + */ + Queue q = new ArrayDeque<>(); + int[] exit = findExit(); + int[][] timeTable = new int[n][n]; + int[][] visited = new int[n][n]; + for(int i=0; i= timeTable[entry[0]][entry[1]]) return -1; // 이동이 불가능한 경우 + q.offer(new int[] {entry[0], entry[1], time}); + visited[entry[0]][entry[1]] = time; + + while(!q.isEmpty()) { + int[] cur = q.poll(); + int curX = cur[0]; + int curY = cur[1]; + int curTime = cur[2]; + + if(curX == exit[0] && curY == exit[1]) return curTime; + + for(int dir=0; dir<4; dir++) { + int nx = curX + dx[dir]; + int ny = curY + dy[dir]; + + if(nx < 0 || ny < 0 || nx >= n || ny >= n) continue; + if(miziBoard[nx][ny] == 1 || miziBoard[nx][ny] == 3) continue; + if(visited[nx][ny] <= curTime + 1 || timeTable[nx][ny] <= curTime + 1) continue; + visited[nx][ny] = curTime + 1; + q.offer(new int[] {nx, ny, curTime+1}); + } + } + + return -1; + } + static int[] findExit() { + for(int x=0; x= n || y>= n) break; + if(miziBoard[x][y] != 0) break; + timeTable[x][y] = Math.min(depth * time, timeTable[x][y]); + depth++; + } + } + } +} diff --git "a/java/src/feb/week3/codetree/\354\275\224\353\223\234\355\212\270\353\246\254DB_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/codetree/\354\275\224\353\223\234\355\212\270\353\246\254DB_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..287030f --- /dev/null +++ "b/java/src/feb/week3/codetree/\354\275\224\353\223\234\355\212\270\353\246\254DB_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,242 @@ +package feb.week3.codetree; + +import java.util.*; +import java.io.*; + +public class 코드트리DB_박재환 { + static BufferedReader br; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + System.out.println(init()); + br.close(); + } + static final String INIT = "init"; + static final String DELETE = "delete"; + static final String INSERT = "insert"; + static final String RANK = "rank"; + static final String SUM = "sum"; + static class Command { + String cmd; + String name; + int value; + int k; + + // INSERT + Command(String cmd, String name, int value) { + this.cmd = cmd; + this.name = name; + this.value = value; + } + // DELETE + Command(String cmd, String name) { + this.cmd = cmd; + this.name = name; + } + // RANK, SUM + Command(String cmd, int k) { + this.cmd = cmd; + this.k = k; + } + } + static StringTokenizer st; + static StringBuilder sb; + static Queue cmdQ; + static List values; + static String init() throws IOException { + sb = new StringBuilder(); + cmdQ = new ArrayDeque<>(); + values = new ArrayList<>(); + + int queryTime = Integer.parseInt(br.readLine().trim()); + while(queryTime-- > 0) { + st = new StringTokenizer(br.readLine().trim()); + String cmd = st.nextToken(); + if(cmd.equals(INIT)) { + if(!cmdQ.isEmpty()) { // 수행할 명령어가 쌓였다면 실행 + processCommand(); + } + cmdQ.clear(); + values.clear(); + continue; + } + Command command = null; + if(cmd.equals(INSERT)){ + String name = st.nextToken(); + int value = Integer.parseInt(st.nextToken()); + values.add(value); + command = new Command(INSERT, name, value); + } + else if(cmd.equals(DELETE)){ + String name = st.nextToken(); + command = new Command(DELETE, name); + } + else if(cmd.equals(RANK)){ + int k = Integer.parseInt(st.nextToken()); + command = new Command(RANK, k); + } + else if(cmd.equals(SUM)){ + int k = Integer.parseInt(st.nextToken()); + command = new Command(SUM, k); + } + cmdQ.offer(command); + } + if (!cmdQ.isEmpty()) { + processCommand(); + } + return sb.toString(); + } + + static Map nameToValue; + static Map valueToName; + static long[] sumTree; // 누적 합을 기록 + static int[] rankTree; // 순위를 기록 + static void processCommand() { + compress(); + if (size == 0) { // 처리할 INSERT 가 없을 때 + while (!cmdQ.isEmpty()) { + Command cur = cmdQ.poll(); + + if (cur.cmd.equals(RANK)) sb.append("None"); + else if (cur.cmd.equals(SUM)) sb.append(0); + else if (cur.cmd.equals(INSERT)) sb.append(1); + else if (cur.cmd.equals(DELETE)) sb.append(0); + sb.append('\n'); + } + return; + } + nameToValue = new HashMap<>(); + valueToName = new HashMap<>(); + sumTree = new long[4*size]; + rankTree = new int[4*size]; + + while(!cmdQ.isEmpty()) { + Command cur = cmdQ.poll(); + + // UPDATE + if(cur.cmd.equals(INSERT)) { + String name = cur.name; + int value = cur.value; + /** + * name 과 value 는 각 PK + */ + if(nameToValue.get(name) != null) sb.append(0); + else if(valueToName.get(value) != null) sb.append(0); + else { // PK 조건 + int id = valueToId.get(value); + + updateSum(1, 1, size, id, id, value); + updateRank(1, 1, size, id, id, 1); + + nameToValue.put(name, value); + valueToName.put(value, name); + sb.append(1); + } + } + else if(cur.cmd.equals(DELETE)) { + String name = cur.name; + if(nameToValue.get(name) == null) sb .append(0); + else { + int value = nameToValue.get(name); + int id = valueToId.get(value); + + updateSum(1, 1, size, id, id, 0); + updateRank(1, 1, size, id, id, 0); + sb.append(value); + nameToValue.remove(name); + valueToName.remove(value); + } + } + // QUERY + else if(cur.cmd.equals(RANK)) { + int k = cur.k; + if(rankTree[1] < k) sb.append("None"); + else { + int id = rankQuery(1, 1, size, k); + int value = idToValue[id]; + sb.append(valueToName.get(value)); + } + } + else if(cur.cmd.equals(SUM)) { + int k = cur.k; + /** + * k 에 대응하는 원소가 없을 수 있음 + */ + int id = upperBoundId(k); + if(id <= 0) sb.append(0); + else { + sb.append(sumQuery(1, 1, size, 1, id)); + } + } + sb.append('\n'); + } + } + static int size; + static Map valueToId; + static int[] idToValue; + static void compress() { + Set set = new HashSet<>(values); // 중복 제거 (PK) + List sorted = new ArrayList<>(set); + sorted.sort(Integer::compare); + + size = set.size(); + valueToId = new HashMap<>(); + idToValue = new int[size+1]; // 1-based + + for(int i=0; i e) return; + if(l >= s && r <= e) { + sumTree[id] = v; + return; + } + int mid = l + (r-l)/2; + updateSum(2*id, l, mid, s, e, v); + updateSum(2*id+1, mid+1, r, s, e, v); + sumTree[id] = sumTree[2*id] + sumTree[2*id+1]; + } + static void updateRank(int id, int l, int r, int s, int e, int v) { + if(r < s || l > e) return; + if(l >= s && r <= e) { + rankTree[id] = v; + return; + } + int mid = l + (r-l)/2; + updateRank(2*id, l, mid, s, e, v); + updateRank(2*id+1, mid+1, r, s, e, v); + rankTree[id] = rankTree[2*id] + rankTree[2*id+1]; + } + static long sumQuery(int id, int l, int r, int s, int e) { + if(r < s || l > e) return 0; + if(l >= s && r <= e) return sumTree[id]; + + int mid = l + (r-l)/2; + return sumQuery(2*id, l, mid, s, e) + sumQuery(2*id+1, mid+1, r, s, e); + } + static int rankQuery(int id, int l, int r, int k) { + if(l == r) return l; // 리프 노드에 도달 + + int lNodeCnt = rankTree[2*id]; + int mid = l + (r-l)/2; + + if(lNodeCnt >= k) { // 왼쪽에 찾고자하는 원소가 존재 + return rankQuery(2*id, l, mid, k); + } else { + return rankQuery(2*id+1, mid+1, r, k-lNodeCnt); + } + } + static int upperBoundId(int target) { + int l = 1, r = size+1; // 1-based + while(l < r) { + int mid = l + (r-l)/2; + if(target < idToValue[mid]) r = mid; + else l = mid+1; + } + return l-1; // k보다 큰 첫 id니까 1 감소 + } +} diff --git "a/java/src/feb/week3/programmers/\353\263\264\353\254\274\354\247\200\353\217\204_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/programmers/\353\263\264\353\254\274\354\247\200\353\217\204_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..c788631 --- /dev/null +++ "b/java/src/feb/week3/programmers/\353\263\264\353\254\274\354\247\200\353\217\204_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,116 @@ +package feb.week3.programmers; + +import java.util.*; + +public class 보물지도_박재환 { + public static void main(String[] args) { + int n = 5; + int m = 4; + int[][] hole = {{1, 4}, {2, 1}, {2, 2}, {2, 3}, {2, 4}, {3, 3}, {4, 1}, {4, 3}, {5, 3}}; + 보물지도_박재환 problem = new 보물지도_박재환(); + int result = problem.solution(n, m, hole); + System.out.println(result); + } + final int[] dx = {0,1,0,-1}; + final int[] dy = {1,0,-1,0}; + + int[][] board; + int n, m; + public int solution(int n, int m, int[][] hole) { + board = new int[m][n]; + this.n = n; + this.m = m; + /** + * 시작 위치 [1,1] -> [0,0] 으로 변경 + * 한 칸 이동하는데 1 소모 + * + * 보물은 [n-1,m-1] + * + * 신발을 신고 뛰면 한 번에 두 칸을 이동 가능 -> 함정도 건너뛸 수 있음 + * -> 한 번밖에 사용못함 + */ + + for(int[] h : hole) { // 함정 표시 + int x = h[1]-1; + int y = h[0]-1; + board[x][y] = -1; + } + + for(int[] arr : board) System.out.println(Arrays.toString(arr)); + + return findFastRout(); + } + int findFastRout() { + Queue q = new ArrayDeque<>(); + int[][][] visited = new int[m][n][2]; // [x][y][0 / 1] + for(int x=0; x visited[x][y][jump]) continue; + + for(int dir=0; dir<4; dir++) { + int nx = x + dx[dir]; + int ny = y + dy[dir]; + if(nx < 0 || nx >= m || ny < 0 || ny >= n) continue; // 격자를 벗어나는 경우 + if(board[nx][ny] == -1 && jump == 0) { // 함정인 경우 -> 뛰어넘어 + nx += dx[dir]; + ny += dy[dir]; + if(nx < 0 || nx >= m || ny < 0 || ny >= n) continue; // 격자를 벗어나는 경우 + if(board[nx][ny] == -1) continue; // 또 함정인 경우 + if(visited[nx][ny][1] <= move + 1) continue; + visited[nx][ny][1] = move + 1; + q.offer(new int[] {nx, ny, 1, move + 1}); + continue; + } + if(board[nx][ny] == -1) continue; + // 장애물이 없는 경우 + // 점프하거나 그냥 한 칸 이동하거나 가능 + if(visited[nx][ny][jump] > move + 1) { // 현재 상태 그대로 이동 + visited[nx][ny][jump] = move + 1; + q.offer(new int[] {nx, ny, jump, move + 1}); + } + /** + * 일반 평지에서도 점프가 가능 + * -> 가는길에 함정이 없는 경우가 있음 + */ + if(jump == 0) { + nx += dx[dir]; + ny += dy[dir]; + if(nx < 0 || nx >= m || ny < 0 || ny >= n) continue; // 격자를 벗어나는 경우 + if(board[nx][ny] == -1) continue; // 함정인 경우 + if(visited[nx][ny][1] <= move + 1) continue; + visited[nx][ny][1] = move + 1; + q.offer(new int[] {nx, ny, 1, move + 1}); + } + } + } + return -1; + } + + void printMap(int[][][] visited) { + for(int x=0; x(); + for (int x = 0; x < n; x++) { + for (int y = 0; y < n; y++) { + if (board[x][y] == maxHeight) candidateLocation.add(new int[]{x, y}); + } + } + } + + static int n, k; + static int[][] board; + static List candidateLocation; + static int maxLength; + static int init() throws IOException { + input(); + + maxLength = 0; + for (int[] point : candidateLocation) { + /** + * 출발지 후보가 될 수 있는 위치들 + */ + int x = point[0]; + int y = point[1]; + /** + * 시작 위치를 포함해서 길이를 계산 + */ + int curHeight = board[x][y]; + boolean[][] visited = new boolean[n][n]; + visited[x][y] = true; + getLongestPath(x, y, 1, 0, curHeight, visited); + } + return maxLength; + } + + static int[] dx = {0, 1, 0, -1}; + static int[] dy = {1, 0, -1, 0}; + static void getLongestPath(int x, int y, int len, int dig, int curHeight, boolean[][] visited) { + for(int dir=0; dir<4; dir++) { + int nx = x + dx[dir]; + int ny = y + dy[dir]; + if(nx < 0 || ny < 0 || nx >= n || ny >= n) continue; // 격자를 벗어나는 경우 + + /** + * 두 가지 경우가 있음 + * - 현 위치보다 높이가 같거나 높은 경우 + * - 깎을 수 있는 경우 깎아봄 + * - 높이가 낮은 경우 + * - 깎지 않아도 이동 가능 + */ + int nextHeight = board[nx][ny]; + if(curHeight < nextHeight+1 && dig == 0) { // 현 위치보다 높고, 아직 다른 산을 깎지 않은 경우 + // k 만큼 땅을 깎을 수 있음 + int need = nextHeight - (curHeight - 1); // 현재보다 1 낮은 높이로 만들기 위해 필요한 높이 + if(need > k) continue; + if (visited[nx][ny]) continue; + visited[nx][ny] = true; + getLongestPath(nx, ny, len + 1, 1, curHeight-1, visited); + visited[nx][ny] = false; + } else if(curHeight > nextHeight) { // 깎지 않고 이동 가능 + if(visited[nx][ny]) continue; + visited[nx][ny] = true; + getLongestPath(nx, ny, len+1, dig, nextHeight, visited); + visited[nx][ny] = false; + } + } + maxLength = Math.max(len, maxLength); + } +} diff --git "a/java/src/feb/week3/swea/\354\210\230\354\230\201\354\236\245_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/swea/\354\210\230\354\230\201\354\236\245_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..2ca7b90 --- /dev/null +++ "b/java/src/feb/week3/swea/\354\210\230\354\230\201\354\236\245_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,56 @@ +package feb.week3.swea; + +import java.util.*; +import java.io.*; + +public class 수영장_박재환 { + static BufferedReader br; + static StringBuilder sb; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + sb = new StringBuilder(); + int tc = Integer.parseInt(br.readLine().trim()); + for(int i=1; i 1달 이용권의 누적 비용을 기반으로 최소비용 계산 + int[] bestPrice = new int[13]; // 1-based 로 + for(int i=1; i<13; i++) { + /** + * 이전 달까지의 최적 값 + 이번달 1달 이용권 요금 + */ + bestPrice[i] = bestPrice[i-1] + oneMonthPay[i]; + if (i >= 3) { + /** + * [이전 달까지의 최적 값 + 이번달 1달 이용권 요금] vs. [이번달을 포함한 3개월 이용권 + 이번달 - 3달 전 최적 값] + * + * 각 id 마다, 분기처리 적용 + */ + bestPrice[i] = Math.min(bestPrice[i], bestPrice[i - 3] + prices[2]); + } + } + + return Math.min(prices[3], bestPrice[12]); + } +} diff --git "a/java/src/feb/week3/swea/\354\236\205\352\265\255\354\213\254\354\202\254_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/swea/\354\236\205\352\265\255\354\213\254\354\202\254_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..17c30e7 --- /dev/null +++ "b/java/src/feb/week3/swea/\354\236\205\352\265\255\354\213\254\354\202\254_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,77 @@ +package feb.week3.swea; + +import java.util.*; +import java.io.*; + +public class 입국심사_박재환 { + /** + * 1 ~ N번의 입국심사대 + * k 번 입국심사대에서 걸리는 시간 t[k] + * -> 심사 끝나면 지연없이 다음사람 심사 + * + * 초기에는 모두 비어있음 + * 모두 심사를 받기위해 걸리는 최소 시간 + * + * 동시간에 빠지고, 심사 시작 가능 + */ + static BufferedReader br; + static StringBuilder sb; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + sb = new StringBuilder(); + int tc = Integer.parseInt(br.readLine().trim()); + for(int i=0; i 입력 범위가 너무 큼 + * + * [문제] + * 모든 인원이 심사를 받기위해 걸리는 최소 시간을 구해라 + * => 시간 T 안에 모든 인원 처리가 가능한가? => Yes / No + * => 시간 T 가 커질 수록 가능성이 증가/감소 한다. => 단조 + */ + // 이분탐색을 이용해 T 를 구한다. + // 각 심사대가 T 시간에 처리할 수 있는 인원 T/table[k] + long l = 0, r = (long)maxTime * m; + long totalTime = r; + while(l <= r) { + long mid = l + (r-l)/2; + int sum = 0; + for(int i : tables) { + sum += (mid / i); + if(sum >= m) break; // 가지치기 + } + if(sum >= m) { // 해당 시간안에 처리할 수 있다면 -> 시간을 줄여본다. + r = mid - 1; + totalTime = Math.min(totalTime, mid); + } else { + l = mid + 1; + } + } + return totalTime; + } +} diff --git "a/java/src/feb/week3/swea/\355\224\204\353\241\234\354\204\270\354\204\234\354\227\260\352\262\260\355\225\230\352\270\260_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/swea/\355\224\204\353\241\234\354\204\270\354\204\234\354\227\260\352\262\260\355\225\230\352\270\260_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..c52bd64 --- /dev/null +++ "b/java/src/feb/week3/swea/\355\224\204\353\241\234\354\204\270\354\204\234\354\227\260\352\262\260\355\225\230\352\270\260_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,92 @@ +package feb.week3.swea; + +import java.util.*; +import java.io.*; + +public class 프로세서연결하기_박재환 { + static BufferedReader br; + static StringBuilder sb; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + sb = new StringBuilder(); + int tc = Integer.parseInt(br.readLine().trim()); + for(int i=0; i processors; + static int minCableL; + static int maxProcessorCnt; + static int init() throws IOException { + processors = new ArrayList<>(); + minCableL = Integer.MAX_VALUE; + maxProcessorCnt = 0; + + n = Integer.parseInt(br.readLine().trim()); + board = new int[n][n]; + for(int x=0; x cableL) { + minCableL = cableL; + } + return; + } + /** + * [가지치기] + * 이전 최대 프로세서 수보다, 지금부터 모든 프로세스가 연결되도 경우가 되지 않는 경우 + */ + if(processorCnt + (processors.size() - processorId) < maxProcessorCnt) + return; + + int[] processor = processors.get(processorId); + // 현재 프로세서를 4방향으로 연결시켜본다. + for(int dir=0; dir<4; dir++) { + /** + * 현재 방향에서 연결 가능한지, 가능하지 않은지 + */ + int x = processor[0]; + int y = processor[1]; + + List cable = new ArrayList<>(); + boolean connect = false; + while(!connect) { + x += dx[dir]; + y += dy[dir]; + if(x < 0 || y < 0 || x >= n || y >= n) break; + if(board[x][y] != 0) break; + if(checked[x][y]) break; + checked[x][y] = true; + cable.add(new int[] {x, y}); + if(x == 0 || x == n-1 || y == 0 || y == n-1) { + connect = true; + } + } + if(connect) { + findMinCableL(processorId+1, cableL + cable.size(), processorCnt + 1, checked); + } + for(int[] point : cable) checked[point[0]][point[1]] = false; + } + findMinCableL(processorId + 1, cableL, processorCnt, checked); + } +} diff --git "a/java/src/feb/week3/swea/\355\230\201\354\247\204\354\235\264\354\235\230\355\224\204\353\241\234\352\267\270\353\236\250\352\262\200\354\246\235_\353\260\225\354\236\254\355\231\230.java" "b/java/src/feb/week3/swea/\355\230\201\354\247\204\354\235\264\354\235\230\355\224\204\353\241\234\352\267\270\353\236\250\352\262\200\354\246\235_\353\260\225\354\236\254\355\231\230.java" new file mode 100644 index 0000000..2223ef8 --- /dev/null +++ "b/java/src/feb/week3/swea/\355\230\201\354\247\204\354\235\264\354\235\230\355\224\204\353\241\234\352\267\270\353\236\250\352\262\200\354\246\235_\353\260\225\354\236\254\355\231\230.java" @@ -0,0 +1,108 @@ +package feb.week3.swea; + +import java.util.*; +import java.io.*; + +public class 혁진이의프로그램검증_박재환 { + static BufferedReader br; + static StringBuilder sb; + public static void main(String[] args) throws IOException { + br = new BufferedReader(new InputStreamReader(System.in)); + sb = new StringBuilder(); + int tc = Integer.parseInt(br.readLine().trim()); + for(int i=0; i q = new ArrayDeque<>(); + // 메모리의 범위는 0 ~ 15 + boolean[][][][] visited = new boolean[r][c][4][16]; + + // 초기 위치 설정 + q.offer(new State(0,0,0,0)); + visited[0][0][0][0] = true; + + while(!q.isEmpty()) { + State cur = q.poll(); + int x = cur.x; + int y = cur.y; + int dir = cur.dir; + int memory = cur.memory; + + char cmd = board[cur.x][cur.y]; + + if(cmd == '@') return true; // 종료 조건 + + if('0' <= cmd && '9' >= cmd) memory = cmd - '0'; + else if('+' == cmd) memory = (memory+1)%16; + else if('-' == cmd) memory = (memory-1+16)%16; + else if('>' == cmd) dir = 0; + else if('v' == cmd) dir = 1; + else if('<' == cmd) dir = 2; + else if('^' == cmd) dir = 3; + else if('_' == cmd) dir = memory > 0 ? 2 : 0; + else if('|' == cmd) dir = memory > 0 ? 3 : 1; + + if('?' == cmd) { + for(int d=0; d<4; d++) { + int nx = (x + dx[d] + r) % r; + int ny = (y + dy[d] + c) % c; + if (!visited[nx][ny][d][memory]) { + visited[nx][ny][d][memory] = true; + q.offer(new State(nx, ny, d, memory)); + } + } + continue; + } + + int nx = (x + dx[dir] + r) % r; + int ny = (y + dy[dir] + c) % c; + + if (!visited[nx][ny][dir][memory]) { + visited[nx][ny][dir][memory] = true; + q.offer(new State(nx, ny, dir, memory)); + } + } + return false; + } +} diff --git "a/python/src/feb/week3/boj/\353\221\220\354\232\251\354\225\241_\353\260\225\354\236\254\355\231\230.py" "b/python/src/feb/week3/boj/\353\221\220\354\232\251\354\225\241_\353\260\225\354\236\254\355\231\230.py" new file mode 100644 index 0000000..c43a771 --- /dev/null +++ "b/python/src/feb/week3/boj/\353\221\220\354\232\251\354\225\241_\353\260\225\354\236\254\355\231\230.py" @@ -0,0 +1,36 @@ +def solution(): + import sys + input = sys.stdin.readline + + n = int(input().strip()) + arr = list(map(int, input().split())) + arr.sort() + + l = 0 + r = n-1 + + best_min = float("inf") + best_max = -float("inf") + best_sum = float("inf") + + while l < r: + a = arr[l] + b = arr[r] + abs_sum = abs(a + b) + if(abs_sum < best_sum): + best_min = a + best_max = b + best_sum = abs_sum + + if abs_sum == 0: + break + + if a + b > 0: + r-=1 + else: + l+=1 + + print(best_min, best_max) + +if __name__ == '__main__': + solution() \ No newline at end of file diff --git "a/python/src/feb/week3/boj/\354\210\230\354\227\264\352\263\274\354\277\274\353\246\25437_\353\260\225\354\236\254\355\231\230.py" "b/python/src/feb/week3/boj/\354\210\230\354\227\264\352\263\274\354\277\274\353\246\25437_\353\260\225\354\236\254\355\231\230.py" new file mode 100644 index 0000000..88997d8 --- /dev/null +++ "b/python/src/feb/week3/boj/\354\210\230\354\227\264\352\263\274\354\277\274\353\246\25437_\353\260\225\354\236\254\355\231\230.py" @@ -0,0 +1,59 @@ +def solution(): + import sys + input = sys.stdin.readline + + n = int(input()) + arr = list(map(int, input().strip().split())) + + tree = [0 for _ in range(4*n)] + def build_tree(id, l, r): + if l == r: + if arr[l]%2 == 0: + tree[id]+=1 + return tree[id] + + mid = (l+r)//2 + tree[id] = build_tree(2*id, l, mid) + build_tree(2*id+1, mid+1, r) + return tree[id] + + def update(id, l, r, s, e, v): + if r < s or l > e: + return + if l >= s and r <= e: + prev = arr[s] + tree[id] += -1 if prev%2 == 0 else 0 + tree[id] += 1 if v % 2 == 0 else 0 + arr[s] = v + return + mid = (l + r) // 2 + update(2*id, l, mid, s, e, v) + update(2*id+1, mid+1, r, s, e, v) + tree[id] = tree[2*id] + tree[2*id+1] + + def query(id, l, r, s, e): + if r < s or l > e: + return 0; + if l >= s and r <= e: + return tree[id] + mid = (l + r) // 2 + return query(2*id, l, mid, s, e) + query(2*id+1, mid+1, r, s, e) + + build_tree(1, 0, n-1) + cmd_count = int(input()) + for _ in range(cmd_count): + cmd, a, b = map(int, input().strip().split()) + + if cmd == 1: + a-=1 + update(1, 0, n-1, a, a, b) + elif cmd == 2: + a-=1 + b-=1 + print(query(1, 0, n-1, a, b)) + else: + a -= 1 + b -= 1 + print((b-a+1)-query(1, 0, n - 1, a, b)) + +if __name__ == '__main__': + solution() \ No newline at end of file diff --git "a/python/src/feb/week3/boj/\354\227\220\353\260\224\354\277\260_\353\260\225\354\236\254\355\231\230.py" "b/python/src/feb/week3/boj/\354\227\220\353\260\224\354\277\260_\353\260\225\354\236\254\355\231\230.py" new file mode 100644 index 0000000..4f74462 --- /dev/null +++ "b/python/src/feb/week3/boj/\354\227\220\353\260\224\354\277\260_\353\260\225\354\236\254\355\231\230.py" @@ -0,0 +1,73 @@ +def solution(): + import sys + sys.setrecursionlimit(10**6) + input = sys.stdin.readline + + n, q1, q2 = map(int, input().split()) + arr = [0] + list(map(int, input().split())) + + tree = [0 for _ in range(4*(n+1))] + lazy = [0 for _ in range(4*(n+1))] + + def build_tree(id, l, r): + if l == r: + tree[id] = arr[l] + return tree[id] + mid = (l + r) // 2 + tree[id] = build_tree(2 * id, l, mid) + build_tree(2 * id + 1, mid + 1, r) + return tree[id] + + def push(id, l, r): + if lazy[id] == 0 or l == r: + return + + temp = lazy[id] + mid = (l + r) // 2 + tree[2 * id] = tree[2*id] + (temp * (mid - l + 1)) + lazy[2 * id] += temp + tree[2 * id + 1] = tree[2*id+1] + (temp * (r - mid)) + lazy[2 * id + 1] += temp + + lazy[id] = 0 + + def update(id, l, r, s, e, v): + if r < s or l > e: + return + if l >= s and r <= e: + tree[id] = tree[id] + (v * (r - l + 1)) + lazy[id] += v + return + push(id, l, r) + mid = (l + r) // 2 + update(2 * id, l, mid, s, e, v) + update(2 * id + 1, mid + 1, r, s, e, v) + tree[id] = tree[2 * id] + tree[2 * id + 1] + + def query(id, l, r, s, e): + if r < s or l > e: + return 0 + if l >= s and r <= e: + return tree[id] + push(id, l, r) + mid = (l + r) // 2 + return query(2 * id, l, mid, s, e) + query(2 * id + 1, mid + 1, r, s, e) + + build_tree(1, 1, n) + out = [] + while q1 + q2 > 0: + cmd = list(map(int, input().split())) + if cmd[0] == 1: + s = min(cmd[1], cmd[2]) + e = max(cmd[1], cmd[2]) + out.append(str(query(1, 1, n, s, e))) + q2-=1 + elif cmd[0] == 2: + s = min(cmd[1], cmd[2]) + e = max(cmd[1], cmd[2]) + v = cmd[3] + update(1, 1, n, s, e, v) + q1 -= 1 + sys.stdout.write('\n'.join(out)) + +if __name__ == '__main__': + solution() \ No newline at end of file diff --git "a/python/src/feb/week3/boj/\354\232\251\354\225\241_\353\260\225\354\236\254\355\231\230.py" "b/python/src/feb/week3/boj/\354\232\251\354\225\241_\353\260\225\354\236\254\355\231\230.py" new file mode 100644 index 0000000..ba06923 --- /dev/null +++ "b/python/src/feb/week3/boj/\354\232\251\354\225\241_\353\260\225\354\236\254\355\231\230.py" @@ -0,0 +1,39 @@ +def solution(): + import sys + input = sys.stdin.readline + + n = int(input()) + arr = list(map(int, input().strip().split())) + + l = 0 + r = n-1 + + sum_best = 2 * 1_000_000_000 + l_best = 0 + r_best = 0 + + while l < r: + sum = arr[l] + arr[r] + + if sum == 0: + sum_best = sum + l_best = arr[l] + r_best = arr[r] + break + + if sum > 0: + if abs(sum) < abs(sum_best): + sum_best = sum + l_best = arr[l] + r_best = arr[r] + r-=1 + else: + if abs(sum) < abs(sum_best): + sum_best = sum + l_best = arr[l] + r_best = arr[r] + l+=1 + print(l_best, r_best) + +if __name__ == '__main__': + solution() \ No newline at end of file diff --git "a/python/src/feb/week3/swea/\354\210\230\354\230\201\354\236\245_\353\260\225\354\236\254\355\231\230.py" "b/python/src/feb/week3/swea/\354\210\230\354\230\201\354\236\245_\353\260\225\354\236\254\355\231\230.py" new file mode 100644 index 0000000..98a1bcd --- /dev/null +++ "b/python/src/feb/week3/swea/\354\210\230\354\230\201\354\236\245_\353\260\225\354\236\254\355\231\230.py" @@ -0,0 +1,33 @@ +def solution(): + """ + SWEA 에서는 sys import 불가 + 그냥 input() 사용 + """ + import sys + input = sys.stdin.readline + + tc = int(input().strip()) + + def get_min_cost(): + prices = list(map(int, input().strip().split())) + month = [0] + list(map(int, input().strip().split())) + + dp = [0 for _ in range(13)] + for i in range(1, 13): + """ + 1일 이용권과, 1달 이용권을 비교해, 최적해 누적 + """ + dp[i] = dp[i-1] + min(month[i] * prices[0], prices[1]) + """ + 1달 이용권과, 3달 이용권 비교해, 최적해 누적 + """ + if i >= 3: # 3달 이용권을 구매할 수 있는 시점부터 + dp[i] = min(dp[i], dp[i-3] + prices[2]) + return min(dp[12], prices[3]) + + for i in range(1, tc+1): + result = get_min_cost() + print(f"#{i} {result}") + +if __name__ == '__main__': + solution() \ No newline at end of file diff --git "a/python/src/feb/week3/swea/\354\236\205\352\265\255\354\213\254\354\202\254_\353\260\225\354\236\254\355\231\230.py" "b/python/src/feb/week3/swea/\354\236\205\352\265\255\354\213\254\354\202\254_\353\260\225\354\236\254\355\231\230.py" new file mode 100644 index 0000000..cbf7012 --- /dev/null +++ "b/python/src/feb/week3/swea/\354\236\205\352\265\255\354\213\254\354\202\254_\353\260\225\354\236\254\355\231\230.py" @@ -0,0 +1,32 @@ +def solution(): + tc = int(input()) + def find_min_time(): + n, m = map(int, input().strip().split()) + arr = list(int(input().strip()) for _ in range(n)) + + l = 0 + r = m * max(arr) + """ + [Lower Bound] + 목표보다 크거나 같은 값 중 최소값 + """ + while l < r: + mid = (l+r)//2 + + sum = 0 + for i in arr: + sum += (mid//i) + if sum >= m: + break + if sum >= m: + r = mid + else: + l = mid+1 + + return l + for i in range(tc): + result = find_min_time() + print(f'#{i+1} {result}') + +if __name__ == '__main__': + solution() \ No newline at end of file