-
Notifications
You must be signed in to change notification settings - Fork 1
[유병규-13주차 알고리즘 스터디] #64
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
base: main
Are you sure you want to change the base?
Changes from all commits
8604743
d39bd2e
27ed4e3
71daa97
2baf26b
6016841
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| import java.io.*; | ||
| import java.util.*; | ||
|
|
||
| public class Main { | ||
|
|
||
| public static void main(String[] args) throws IOException { | ||
| BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); | ||
|
|
||
| //입력 값 | ||
| int n = Integer.parseInt(br.readLine()); | ||
| int[] a = new int[n]; | ||
| int[] b = new int[n]; | ||
|
|
||
| StringTokenizer st = new StringTokenizer(br.readLine()); | ||
| for(int i=0; i<n; i++) { | ||
| a[i] = Integer.parseInt(st.nextToken()); | ||
| } | ||
| st = new StringTokenizer(br.readLine()); | ||
| for(int i=0; i<n; i++) { | ||
| b[i] = Integer.parseInt(st.nextToken()); | ||
| } | ||
|
|
||
| //바늘의 각도가 특정 순서대로 주어지지 않아, 정렬 | ||
| Arrays.sort(a); | ||
| Arrays.sort(b); | ||
|
|
||
| //a 배열의 각 바늘 간 사잇각을 저장하는 배열 | ||
| int[] da = new int[n]; | ||
| int pre = 0; | ||
| for(int i=1; i<n; i++) { | ||
| da[i-1] = a[i]-a[pre++]; | ||
| } | ||
| //첫 바늘과 마지막 바늘의 사잇각 | ||
| da[n-1] = a[0]+360000 - a[n-1]; | ||
|
|
||
| //b 배열의 각 바늘 간 사잇각을 저장하는 배열 | ||
| int[] db = new int[n]; | ||
| pre = 0; | ||
| for(int i=1; i<n; i++) { | ||
| db[i-1] = b[i]-b[pre++]; | ||
| } | ||
| //첫 바늘과 마지막 바늘의 사잇각 | ||
| db[n-1] = b[0]+360000 - b[n-1]; | ||
|
|
||
| //실패 함수: pi배열 | ||
| //pi[i]: 0~i까지의 부분 문자열에서 접두사와 접미사의 최대 길이 | ||
| int[] pi = new int[n]; | ||
|
|
||
| int j=0; | ||
| for(int i=1; i<n; i++) { | ||
| while(j>0 && da[i] != da[j]) { | ||
| j = pi[j-1]; | ||
| } | ||
| if(da[i] == da[j]) { | ||
| pi[i] = ++j; | ||
| } | ||
| } | ||
|
|
||
| //모든 점을 시작점으로 비교하기 위한 배열 | ||
| int[] target = new int[2*n]; | ||
| for(int i=0; i<2*n; i++){ | ||
| target[i] = db[i%n]; | ||
| } | ||
|
|
||
| //kmp | ||
| j=0; | ||
| for(int i=0; i<2*n; i++) { | ||
| while(j>0 && target[i] != da[j]) { | ||
| j = pi[j-1]; | ||
| } | ||
|
|
||
| if(target[i] == da[j]) { | ||
| //찾았다면 종료 | ||
| if(j == n-1) { | ||
| System.out.println("possible"); | ||
| return; | ||
| } | ||
| j++; | ||
| } | ||
| } | ||
|
|
||
| System.out.println("impossible"); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| import java.io.*; | ||
| import java.util.*; | ||
|
|
||
| public class Main { | ||
|
|
||
| public static void main(String[] args) throws IOException { | ||
| BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); | ||
|
|
||
| int n = Integer.parseInt(br.readLine()); | ||
| //최소 거리 정보 | ||
| int[][] map = new int[n][n]; | ||
| //현재 도로 정보 | ||
| int[][] graph = new int[n][n]; | ||
| for(int i=0; i<n; i++) { | ||
| StringTokenizer st = new StringTokenizer(br.readLine()); | ||
| for(int j=0; j<n; j++) { | ||
| map[i][j] = graph[i][j] = Integer.parseInt(st.nextToken()); | ||
| } | ||
| } | ||
|
|
||
| //필수 도로의 시간의 합 = 결과 값 | ||
| int result = 0; | ||
| //다익스트라를 위한 자료 구조 | ||
| int[] dist = new int[n]; | ||
| PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> Integer.compare(dist[o1], dist[o2])); | ||
| //모든 도로를 하나씩 제거해보며 해당 도로가 최소한 있어야 하는 필수 도로인지 판별 | ||
| //i와 j를 잇는 도로를 제거했을 때 i -> j로 가는 최단 거리를 계산 | ||
| for(int i=0; i<n; i++) { | ||
| for(int j=i+1; j<n; j++) { | ||
| //다익스트라를 위한 초기화 | ||
| pq.clear(); | ||
| Arrays.fill(dist, (int) 1e9); | ||
| //i에서 출발 | ||
| dist[i] = 0; | ||
| boolean[] visited = new boolean[n]; | ||
| pq.offer(i); | ||
|
|
||
| while(!pq.isEmpty()) { | ||
| int current = pq.poll(); | ||
|
|
||
| if(visited[current]) continue; | ||
| visited[current] = true; | ||
| //j까지의 최단거리만 구하면 됨 | ||
| if(current == j) break; | ||
|
|
||
| for(int node=0; node<n; node++) { | ||
| if(visited[node]) continue; | ||
| //i와 j를 잇는 도로는 없다는 가정 | ||
| if(current==i && node==j) continue; | ||
| if(current==j && node==i) continue; | ||
|
|
||
| if(dist[node] <= dist[current] + graph[current][node]) continue; | ||
| dist[node] = dist[current] + graph[current][node]; | ||
|
|
||
| pq.offer(node); | ||
| } | ||
| } | ||
|
|
||
| //i와 j를 잇는 도로를 제거했을 때 i -> j로 가는 최단 거리가 다음과 같을 때 | ||
| //1. 제거 전보다 클 때 해당 도로는 필수 도로이다. | ||
| if(dist[j] > map[i][j]) { | ||
| result += map[i][j]; | ||
| // System.out.println((i+1)+"->"+(j+1)); | ||
| continue; | ||
| } | ||
| //2. 제거 전보다 작다면 문제가 잘못됨. | ||
| if(dist[j] < map[i][j]) { | ||
| System.out.println(-1); | ||
| return; | ||
| } | ||
| //3. 제거 전과 같다면 해당 도로는 필수 도로가 아니므로 도로 정보를 INF 값으로 설정하여 없는 도로로 설정 | ||
| graph[i][j] = (int) 1e9; | ||
| graph[j][i] = (int) 1e9; | ||
| } | ||
| } | ||
|
|
||
| System.out.println(result); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| import java.io.*; | ||
| import java.util.*; | ||
|
|
||
| public class Main { | ||
| private static int n, m; | ||
| private static int[] cost; | ||
|
|
||
| public static void main(String[] args) throws IOException { | ||
| BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); | ||
|
|
||
| StringTokenizer st = new StringTokenizer(br.readLine()); | ||
| n = Integer.parseInt(st.nextToken()); | ||
| m = Integer.parseInt(st.nextToken()); | ||
|
|
||
| st = new StringTokenizer(br.readLine()); | ||
|
|
||
| //cost[i]: i번 놀이기구 운행 시간 | ||
| cost = new int[m+1]; | ||
| long min = Integer.MAX_VALUE; //가장 작은 운행 시간 | ||
| for(int i=1; i<=m; i++) { | ||
| cost[i] = Integer.parseInt(st.nextToken()); | ||
| min = Math.min(min, cost[i]); | ||
| } | ||
|
|
||
| //놀이기구 개수보다 사람이 적다면 바로 반환 | ||
| if(n <= m) { | ||
| System.out.println(n); | ||
| return; | ||
| } | ||
|
|
||
| //이분 탐색(모든 사람이 탈 수 있는 최소 시간) | ||
| long left = 0; | ||
| long right = n*min;//모두 운행시간이 가장 작은 놀이기구를 탔을 때의 시간 | ||
| long minTime = right;//찾고자 하는 최소 시간 | ||
|
|
||
| while(left <= right) { | ||
| long mid = left + (right-left)/2; | ||
|
|
||
| long count = counting(mid); | ||
| if(count >= n) { | ||
| minTime = Math.min(minTime, mid); | ||
| right = mid-1; | ||
| continue; | ||
| } | ||
| left = mid+1; | ||
| } | ||
|
|
||
| //모든 사람이 탈 수 있는 시간의 1분 전에 총 몇 명이 탑승할 수 있는지 | ||
| long count = 0; | ||
| long time = minTime-1; | ||
| //wait[i]: 다음 사람이 i번 놀이기구를 타기 위해 기다려야 하는 대기 시간 | ||
| long[] wait = new long[m+1]; | ||
| for(int i=1; i<=m; i++) { | ||
| //사람 수 계산 | ||
| if(time/cost[i] == 0) count += 1; | ||
| else if(time%cost[i] == 0) count += time/cost[i]; | ||
| else count += time/cost[i]+1; | ||
| //대기 시간 계산 | ||
| wait[i] = time%cost[i] == 0 ? 0 : cost[i] - time%cost[i]; | ||
| } | ||
|
|
||
| //남은 사람 수 | ||
| long rest = n-count; | ||
| //대기 시간이 가장 짧은 순으로 놀이기구 번호 정렬 | ||
| PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> { | ||
| if(wait[o1] == wait[o2]) return Integer.compare(o1, o2); | ||
| return Long.compare(wait[o1], wait[o2]); | ||
| }); | ||
| for(int i=1; i<=m; i++) { | ||
| pq.add(i); | ||
| } | ||
|
|
||
| //마지막 사람이 타는 놀이기구 번호 | ||
| long result = 0; | ||
| for(int i=0; i<rest; i++) { | ||
| result = pq.poll(); | ||
| } | ||
| System.out.println(result); | ||
|
Comment on lines
+63
to
+78
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분을 PQ 없이 구현하면 조금 더 효율적인 코드가 될 것 같습니다!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 확실히 단순 정렬이라 PQ를 사용하지 않고 구현하는 것이 더 효율적인 것 같습니다! 당시에 드디어 풀었다는 기쁨에 당장 생각나는 방법으로 구현했었는데, 좀 더 차분히 문제를 푸는 연습이 필요한 것 같습니다. 리뷰 감사합니다! |
||
| } | ||
|
|
||
| private static long counting(long time) { | ||
| //time 동안 탈 수 있는 총 사람 수 계산 | ||
| long count = 0; | ||
| for(int i=1; i<=m; i++) { | ||
| if(time/cost[i] == 0) count += 1; | ||
| else if(time%cost[i] == 0) count += time/cost[i]; | ||
| else count += time/cost[i]+1; | ||
| } | ||
|
|
||
| return count; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| import java.io.*; | ||
| import java.util.*; | ||
|
|
||
| public class Main { | ||
|
|
||
| public static void main(String[] args) throws IOException { | ||
| BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); | ||
|
|
||
| //초기화 | ||
| int n = Integer.parseInt(br.readLine()); | ||
|
|
||
| List<Set<Integer>> graph = new ArrayList<>(); | ||
| for(int i=0; i<=n; i++) { | ||
| graph.add(new HashSet<>()); | ||
| } | ||
| for(int i=0; i<n-1; i++) { | ||
| StringTokenizer st = new StringTokenizer(br.readLine()); | ||
| int a = Integer.parseInt(st.nextToken()); | ||
| int b = Integer.parseInt(st.nextToken()); | ||
| graph.get(a).add(b); | ||
| graph.get(b).add(a); | ||
| } | ||
|
|
||
| StringTokenizer st = new StringTokenizer(br.readLine()); | ||
| int[] order = new int[n]; | ||
| for(int i=0; i<n; i++) { | ||
| order[i] = Integer.parseInt(st.nextToken()); | ||
| } | ||
|
|
||
| //result: 나올 수 있는 순서 집합 | ||
| List<Set<Integer>> result = new ArrayList<>(); | ||
| for(int i=0; i<=n; i++) { | ||
| result.add(new HashSet<>()); | ||
| } | ||
| // 이 문제는 1부터 시작 | ||
| result.get(0).add(1); | ||
| //result 채우기 | ||
| boolean[] visited = new boolean[n+1]; | ||
| visited[1] = true; | ||
| for(int i=0,idx=1; i<n; i++,idx++) { | ||
| Set<Integer> set = graph.get(order[i]); | ||
| for(int node : set) { | ||
| if(visited[node]) continue; | ||
| visited[node] = true; | ||
| result.get(idx).add(node); | ||
| } | ||
| } | ||
|
|
||
| //result(순서 집합)에 맞게 나오는지 체크 | ||
| int idx = 0; | ||
| for(int i=0; i<n; i++) { | ||
| Set<Integer> set = result.get(i); | ||
| if(set.size() == 0) break; | ||
| for(int j=0; j<set.size(); j++) { | ||
| if(set.contains(order[idx])) { | ||
| idx++; | ||
| continue; | ||
| } | ||
| //만약 해당 순서에 나올 수 없는 숫자라면 0을 리턴 | ||
| System.out.println(0); | ||
| return; | ||
| } | ||
| } | ||
| //전부 통과했다면 1을 리턴 | ||
| System.out.println(1); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
complete graph를 미리 만들어놓고 간선을 하나씩 제거하는 방법도 있었군요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 참고해서 해결한 문제인데, 모든 간선을 하나씩 제거했을 때 이 간선이 최단 거리를 위해서 반드시 필요한 간선인 지 확인하는 방법이 참신하다고 생각했습니다. 여러 간선을 조합과 같이 선택해서 확인하는 것이 아니라 각각의 간선을 확인해서 필요한 간선이면 제거하지 않고 다음 다리를 판단하는 것이 인상적인 풀이 방법이었습니다.