[문제]

숌 회사에서 이번에 새로운 전략 시뮬레이션 게임 세준 크래프트를 개발하기로 하였다. 핵심적인 부분은 개발이 끝난 상태고, 종족별 균형과 전체 게임 시간 등을 조절하는 부분만 남아 있었다.

게임 플레이에 들어가는 시간은 상황에 따라 다를 수 있기 때문에, 모든 건물을 짓는데 걸리는 최소의 시간을 이용하여 근사하기로 하였다. 물론, 어떤 건물을 짓기 위해서 다른 건물을 먼저 지어야 할 수도 있기 때문에 문제가 단순하지만은 않을 수도 있다. 예를 들면 스타크래프트에서 벙커를 짓기 위해서는 배럭을 먼저 지어야 하기 때문에, 배럭을 먼저 지은 뒤 벙커를 지어야 한다. 여러 개의 건물을 동시에 지을 수 있다.

편의상 자원은 무한히 많이 가지고 있고, 건물을 짓는 명령을 내리기까지는 시간이 걸리지 않는다고 가정하자.

 

[입력]

첫째 줄에 건물의 종류 수 N(1 ≤ N ≤ 500)이 주어진다. 다음 N개의 줄에는 각 건물을 짓는데 걸리는 시간과 그 건물을 짓기 위해 먼저 지어져야 하는 건물들의 번호가 주어진다. 건물의 번호는 1부터 N까지로 하고, 각 줄은 -1로 끝난다고 하자. 각 건물을 짓는데 걸리는 시간은 100,000보다 작거나 같은 자연수이다. 모든 건물을 짓는 것이 가능한 입력만 주어진다.

 

[출력]

N개의 각 건물이 완성되기까지 걸리는 최소 시간을 출력한다.

 

[예제]

예제 입력1 예제 출력1
5
10 -1
10 1 -1
4 1 -1
4 3 1 -1
3 3 -1
10
20
14
18
17

 


>문제 풀이

위상정렬 문제였는데, 이론을 배우긴 했는데 문제로 풀어보는건 처음이라 다른 블로그의 코드를 참고하여 공부하였습니다.

문제 조건

1. 어떤 건물을 짓기 위해 다른 건물을 먼저 지어야 할 수도 있다.

2. 여러 건물을 동시에 지을 수 있다.

순환이(cycle) 없고 상대적 순서를 지켜서 수행해야 한다. -> 위상정렬 (Topological Sort)

inDegree: 선행건물들의 개수가 0인 애들을 큐에 넣어서 실행시간을 넣어놓고 이 건물을 선행 조건으로 하는 건물들을 살펴봐야한다.

선행 건물들 부터 실행시간을(여기서 말한 실행시간은 선행조건을 포함한 건설시간) 저장해놓고 다음 건물을 지어야 함으로 dp적인 요소가 들어갔다고 할 수 있다.(Memoization)

나도 다른 분의 코드와 설명을 참고하여 공부했기 때문에 이상 자세한 설명은 생략한다.

(세부적인 내용은 아래 블로그를 참고하길 바랍니다.)

https://subbak2.tistory.com/53

 

[BOJ 백준] 게임 개발(1516) Java

링크 : https://www.acmicpc.net/problem/1516 1516번: 게임 개발 첫째 줄에 건물의 종류 수 N(1 ≤ N ≤ 500)이 주어진다. 다음 N개의 줄에는 각 건물을 짓는데 걸리는 시간과 그 건물을 짓기 위해 먼저 지어져야

subbak2.tistory.com

 

>전체 코드

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
	static int N;
	static Building[] buildingList;
	static int input;
	static Queue<Integer> queue;
	static StringBuilder sb= new StringBuilder();
	
	public static void main(String[] args) throws IOException{
		BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		
		//1.입력
		N= Integer.parseInt(br.readLine());
		buildingList= new Building[N+1];
        for(int i=1; i<=N; i++{
			buildingList[i]= new Building();
        }
		
		//2. N개의 건물정보 입력
		for(int i=1; i<=N; i++) {
			st= new StringTokenizer(br.readLine());
			
			buildingList[i].time= Integer.parseInt(st.nextToken());
			input= Integer.parseInt(st.nextToken());
			while(input!=-1) {
				buildingList[i].inDegree++;
				buildingList[input].adjList.add(i);
				input= Integer.parseInt(st.nextToken());
			}
		}
		
		//2. 위상정렬 재료 만들기
		//- 선행건물이 없는 경우(inDegree=0)인 경우 큐에 넣기
		queue= new ArrayDeque<Integer>();
		for(int i=1; i<=N; i++) {
			if(buildingList[i].inDegree==0) {
				queue.add(i);
			}
		}
		
		//3. 위상정렬
		while(!queue.isEmpty()) {
			//1) 선행건물이 더이상 없는 녀석을 뽑아서 최종 건설시간에 자기 자신을 더해줌
			int quePoll= queue.poll();
			buildingList[quePoll].result+= buildingList[quePoll].time;
			
			//2) 자신을 지어야 지을 수 있는 건물들에게 선행건물의 건설이 끝났음을 알림
			for(int i: buildingList[quePoll].adjList) {
				//2-1) 선행건물의 개수 빼주기
				buildingList[i].inDegree--;
				//2-2) 타겟건물은 아직 건물을 지을 수 없는 상태
				//->현재 상태에서 타겟 건물의 res는 선행건물의 건설시간 중 최댓값: res를 최댓값으로 갱신시켜야
				buildingList[i].result= Math.max(buildingList[i].result, buildingList[quePoll].result);
				//2-3) 타겟건물의 선행건물 개수가 0이면 큐에 넣기
				if(buildingList[i].inDegree==0) queue.add(i);
			}
		}
		
		for(int i=1; i<=N; i++) {
			sb.append(buildingList[i].result+"\n");
		}
		System.out.println(sb.toString());
		
	}//main
	
	public static class Building{
		int time;		//각 건물 건설시간
		int inDegree;	//건물의 선행조건 개수
		int result;		//선행건물 포함 건설시간
		ArrayList<Integer> adjList; //건물이 지어져야 지을수 있는 건물들
		
		public Building() {
			this.time= 0;
			this.inDegree= 0;
			this.result= 0;
			this.adjList= new ArrayList<Integer>();
		}
	}
        
}

https://www.acmicpc.net/problem/1516

 

1516번: 게임 개발

첫째 줄에 건물의 종류 수 N(1 ≤ N ≤ 500)이 주어진다. 다음 N개의 줄에는 각 건물을 짓는데 걸리는 시간과 그 건물을 짓기 위해 먼저 지어져야 하는 건물들의 번호가 주어진다. 건물의 번호는 1부

www.acmicpc.net

 

[문제]

1번부터 N번까지 번호가 붙여져 있는 학생들에 대하여 두 학생끼리 키를 비교한 결과의 일부가 주어져 있다. 단, N명의 학생들의 키는 모두 다르다고 가정한다. 예를 들어, 6명의 학생들에 대하여 6번만 키를 비교하였고, 그 결과가 다음과 같다고 하자.

1번 학생의 키 < 5번 학생의 키

3번 학생의 키 < 4번 학생의 키

5번 학생의 키 < 4번 학생의 키

4번 학생의 키 < 2번 학생의 키

4번 학생의 키 < 6번 학생의 키

5번 학생의 키 < 2번 학생의 키

이 비교 결과로부터 모든 학생 중에서 키가 가장 작은 학생부터 자신이 몇 번째인지 알 수 있는 학생들도 있고 그렇지 못한 학생들도 있다는 사실을 아래처럼 그림을 그려 쉽게 확인할 수 있다. a번 학생의 키가 b번 학생의 키보다 작다면, a에서 b로 화살표를 그려서 표현하였다.

1번은 5번보다 키가 작고, 5번은 4번보다 작기 때문에, 1번은 4번보다 작게 된다. 그러면 1번, 3번, 5번은 모두 4번보다 작게 된다. 또한 4번은 2번과 6번보다 작기 때문에, 4번 학생은 자기보다 작은 학생이 3명이 있고, 자기보다 큰 학생이 2명이 있게 되어 자신의 키가 몇 번째인지 정확히 알 수 있다. 그러나 4번을 제외한 학생들은 자신의 키가 몇 번째인지 알 수 없다.

학생들의 키를 비교한 결과가 주어질 때, 자신의 키가 몇 번째인지 알 수 있는 학생들이 모두 몇 명인지 계산하여 출력하는 프로그램을 작성하시오.

 

[입력]

첫째 줄에 학생들의 수 N (2 ≤ N ≤ 500)과 두 학생 키를 비교한 횟수 M (0 ≤ M ≤ N(N-1)/2)이 주어진다. 다음 M개의 각 줄에는 두 학생의 키를 비교한 결과를 나타내는 두 양의 정수 a와 b가 주어진다. 이는 번호가 a인 학생이 번호가 b인 학생보다 키가 작은 것을 의미한다.

 

[출력]

자신이 키가 몇 번째인지 알 수 있는 학생이 모두 몇 명인지를 출력한다.

 

[예제]

예제 번호 예제 입력 예제 출력
1 6 6
1 5
3 4
5 4
4 2
4 6
5 2
1
2 6 7
1 3
1 5
3 4
5 4
4 2
4 6
5 2
2
3 6 3
1 2
2 3
4 5
0

 


>문제 풀이

1~N번 까지의 학생들의 키 순서를 상대적으로 비교한 정보가 주어진다.

이 정보를 통해 몇 번째로 키가 큰지 알 수 있는 학생이 몇 명인지 출력하라

어떤 학생에 대하여 자신을 제외한 1~N까지의 학생과 키를 비교할 수 있는지 확인해야한다.

그러나 한 명씩 매번 연산을 한다면 시간초과가 발생한다.

>> 모든 경로를 구해놓고서 1~N까지의 학생들이 가능한지 안한지만 체크해야한다.

플로이드-워셜 알고리즘

: 음의 간선 or 양의 간선에서 최단거리 파악할 수 있음

: 음의 싸이클 있으면 안되고 N<500일 때 다익스트라 보다 효율이 좋다고 알고있다.

::구현::

1) (N과 M을 입력받고,) int[][] distance 배열 선언 후 INF=999999999로 초기화

연결 여부 확인 할 때 비정상값인 경우 거르기 위해서 INF로 초기화 해놓는 것이다.

2) 키 정보값을 입력 받는다.

ex) i번 학생 < j번 학생 인 경우 distance[i][j]= 1;

3) for문을 돌면서 distance[i][j] 와 distance[i][k]+distance[k][j] 중 최소값을 선택한다.

( i는 출발지, j는 목적지, k는 경유지 // distance[i][j]: i에서 j로 가는 거리 )

어느 한쪽이 INF이면 다른 쪽을 선택할 것이고, 둘 다 INF이면 4)번에서 걸러진다.

4) 2중 for문 :: 1~N까지 확인하면서 distance[i][j]나 distance[j][i]가 INF가 아닌 경우를 cnt++카운팅 해서

cnt==N-1인 경우 answer++ 카운팅

//자기 자신 distance[i][j]는 무조건 INF 이다.

//그러니까 자신을 뺀 나머지랑 연결되어 있어야 하는데 이것을 cnt= N-1을 체크해줌으로써 확인이 가능하다.

 

>전체 코드

큰 차이는 없지만 두 가지 방법으로 풀어보았다.

어차피 연결여부만 확인하면 된다는 부분에서, int 보다는 boolean이 더 목적에 맞을 것 같아서 2)번으로도 고쳐본 것이다. 위의 설명을 이해했다면 2)번은 int->boolean이기 때문에 코드만 봐도 이해가 될 것 이다.

1) int[][] 배열 이용

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Scanner;
import java.util.StringTokenizer;

public class Main {
	
	public static void main(String[] args) throws IOException{
		final int INF= 999999999;
		int N, M, answer=0;
		int a, b;
		int[][] distance;
		
		BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		
		st= new StringTokenizer(br.readLine());
		N= Integer.parseInt(st.nextToken());
		M= Integer.parseInt(st.nextToken());
		
		distance= new int[N+1][N+1];
		for(int i=1; i<N+1; i++) {
			Arrays.fill(distance[i], INF);
		}
		
		for(int i=0; i<M; i++) {
			st= new StringTokenizer(br.readLine());
			
			a= Integer.parseInt(st.nextToken());
			b= Integer.parseInt(st.nextToken());
			
			distance[a][b]= 1; //a, b가 서로 연결되어있다.
		}
		
		//플로이드-워셜: 경유지-출발지-도착지 : 3중 for문
		//출발 i, 도착:j, 경유지:k
		for(int k=1; k<=N; k++) {
			for(int i=1; i<=N; i++) {
				for(int j=1; j<=N; j++) {
					distance[i][j]= distance[i][j]<=distance[i][k]+distance[k][j]?
									distance[i][j]:distance[i][k]+distance[k][j];
				}
			}
		}
		
		for(int i=1; i<=N; i++) {
			int cnt=0;
			for(int j=1; j<=N; j++) {
				if(distance[i][j]!=INF || distance[j][i]!=INF) cnt++;
			}
			if(cnt==N-1) answer++;
		}
		
        System.out.println(answer);
	}//main
        
}

2) boolean 배열

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Scanner;
import java.util.StringTokenizer;

public class Main {
	
	public static void main(String[] args) throws IOException{
		int N, M, answer=0;
		int a, b;
		boolean[][] distance;
		
		BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		
		st= new StringTokenizer(br.readLine());
		N= Integer.parseInt(st.nextToken());
		M= Integer.parseInt(st.nextToken());
		
		distance= new boolean[N+1][N+1];
		
		for(int i=0; i<M; i++) {
			st= new StringTokenizer(br.readLine());
			
			a= Integer.parseInt(st.nextToken());
			b= Integer.parseInt(st.nextToken());
			
			distance[a][b]= true; //a, b가 서로 연결되어있다.
		}
		
		//플로이드-워셜: 경유지-출발지-도착지 : 3중 for문
		//출발 i, 도착:j, 경유지:k
		for(int k=1; k<=N; k++) {
			for(int i=1; i<=N; i++) {
				for(int j=1; j<=N; j++) {
					if(distance[i][k]&&distance[k][j]) {
						distance[i][j]= true;
					}
				}
			}
		}
		
		boolean check;
		for(int i=1; i<=N; i++) {
			check= false;
			for(int j=1; j<=N; j++) {
				//i에서 시작해서 연결이 안된 학생이 있다.
				if(i!=j && !distance[i][j] && !distance[j][i]) {
					check= true;
					break;
				}
			}
			if(!check) answer++;
		}
		
        System.out.println(answer);
	}//main
        
}

https://www.acmicpc.net/problem/2458

 

2458번: 키 순서

1번부터 N번까지 번호가 붙여져 있는 학생들에 대하여 두 학생끼리 키를 비교한 결과의 일부가 주어져 있다. 단, N명의 학생들의 키는 모두 다르다고 가정한다. 예를 들어, 6명의 학생들에 대하여

www.acmicpc.net

 

[문제]

도현이는 컴퓨터와 컴퓨터를 모두 연결하는 네트워크를 구축하려 한다. 하지만 아쉽게도 허브가 있지 않아 컴퓨터와 컴퓨터를 직접 연결하여야 한다. 그런데 모두가 자료를 공유하기 위해서는 모든 컴퓨터가 연결이 되어 있어야 한다. (a와 b가 연결이 되어 있다는 말은 a에서 b로의 경로가 존재한다는 것을 의미한다. a에서 b를 연결하는 선이 있고, b와 c를 연결하는 선이 있으면 a와 c는 연결이 되어 있다.)

그런데 이왕이면 컴퓨터를 연결하는 비용을 최소로 하여야 컴퓨터를 연결하는 비용 외에 다른 곳에 돈을 더 쓸 수 있을 것이다. 이제 각 컴퓨터를 연결하는데 필요한 비용이 주어졌을 때 모든 컴퓨터를 연결하는데 필요한 최소비용을 출력하라. 모든 컴퓨터를 연결할 수 없는 경우는 없다.

 

[입력]

첫째 줄에 컴퓨터의 수 N (1 ≤ N ≤ 1000)가 주어진다.

둘째 줄에는 연결할 수 있는 선의 수 M (1 ≤ M ≤ 100,000)가 주어진다.

셋째 줄부터 M+2번째 줄까지 총 M개의 줄에 각 컴퓨터를 연결하는데 드는 비용이 주어진다. 이 비용의 정보는 세 개의 정수로 주어지는데, 만약에 a b c 가 주어져 있다고 하면 a컴퓨터와 b컴퓨터를 연결하는데 비용이 c (1 ≤ c ≤ 10,000) 만큼 든다는 것을 의미한다. a와 b는 같을 수도 있다.

 

[출력]

모든 컴퓨터를 연결하는데 필요한 최소비용을 첫째 줄에 출력한다.

 

[예제]

[힌트]

이 경우에 1-3, 2-3, 3-4, 4-5, 4-6을 연결하면 주어진 output이 나오게 된다.


>문제 이해

모든 컴퓨터를 연결해야하는데,

- 가중치에 따라 최소 비용으로 모든 노선을 연결해야 한다.

만약 컴퓨터 a와 b가 연결되어있고, 컴퓨터 b와 c가 연결되어 있으면, a와 c가 연결되어 있다고 본다.

- 사이클이 없다.

=>> MST(Minimum Spanning Tree: 최소신장트리)의 특징이다.

>문제 풀이

union - find 베이스의 MST- 크루스칼 알고리즘으로 풀었다.

MST의 방법으로는 크루스칼과 프림이 있는데 이 문제에서는 간선의 개수가 적기 때문에 크루스칼 알고리즘을 이용해서 풀었다.

(크루스칼 알고리즘과 프림 알고리즘에 관한 간략한 차이를 아래에 적어놓았다.)

* 함수: union

: a와 b가 연결되어 있으면 둘의 부모를 같게 만들어 주는 과정

* 함수 find(int a)

: a의 조상을 찾아주는 과정이다. a와 b가 바로 연결이 안되어있어도 a의 조상과 b의 조상이 같으면 연결이 된거니까 이를 확인하기 위함이다.

* Main( )

priority queue를 이용해서 cost를 기준으로 input값들을 넣어준다.

그리고 이제 pq값들을 하나씩 꺼내서 최소비용으로 간선을 연결한다.

모든 노드를 연결해야하기 때문에 연결된 간선의 개수는 N-1이 되어야 한다.

간선을 연결할 때 마다 answer++ 카운팅을 해준다.

> 크루스칼 vs 프림

- 크루스칼

: 간선을 기준으로 선택해 나간다. 그래서 사이클이 이루어지는지 확인이 꼭 필요하다.

: 간선의 정렬이 오래 걸릴 수 있다. -> ∴ 간선이 적은 트리에 사용

- 프림

: 시작점을 기준으로 선택해 나가기 때문에 정점 중심 -> 사이클인지 아닌지 확인 할 필요가 없다.

 

>전체 코드

 

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.io.IOException;
import java.util.Arrays;
import java.util.PriorityQueue;

public class Main {
	static int[] parent;
	static PriorityQueue<info> pq;
	
	public static void main(String[] args) throws IOException {
		BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		int N, M;
		N= Integer.parseInt(br.readLine());
		M= Integer.parseInt(br.readLine());
		
		parent= new int[N+1];
		pq= new PriorityQueue<info>();
		for(int i=1; i<=N; i++) parent[i]= i;
		
		int start, target, cost;
		for(int i=0; i<M; i++) {
			st= new StringTokenizer(br.readLine());
			start= Integer.parseInt(st.nextToken());
			target= Integer.parseInt(st.nextToken());
			cost= Integer.parseInt(st.nextToken());
			//cost 순서로 최소힙 정렬
			pq.offer(new info(start, target, cost));
		}
		
		int answer=0, line=0;
		while(!pq.isEmpty()) {
			if(line==N-1) break;
			
			info cur= pq.poll();
			if(cur.start== cur.target) continue; //같은 컴은 연결필요X
			if(find(cur.start)!=find(cur.target)) {
				line++;
				union(cur.start, cur.target);
				answer+= cur.cost;
			}
		}
		System.out.println(answer);
	}//main
	
	//조상찾기
	static int find(int a) {
		if(parent[a]==a) return a;
		return find(parent[a]);
	}
	
	//동맹찾기
	static void union(int a, int b) {
		parent[find(a)]=find(b);
	}
	
	//노드 연결 정보
	static class info implements Comparable<info>{
		int start, target, cost;
		public info(int start, int target, int cost) {
			this.start= start;
			this.target= target;
			this.cost= cost;
		}
		@Override
		public int compareTo(info o) {
			return this.cost-o.cost;
		}
	}
}

https://www.acmicpc.net/problem/1922

 

1922번: 네트워크 연결

이 경우에 1-3, 2-3, 3-4, 4-5, 4-6을 연결하면 주어진 output이 나오게 된다.

www.acmicpc.net

 

[문제]

분수 A/B는 분자가 A, 분모가 B인 분수를 의미한다. A와 B는 모두 자연수라고 하자.

두 분수의 합 또한 분수로 표현할 수 있다. 두 분수가 주어졌을 때, 그 합을 기약분수의 형태로 구하는 프로그램을 작성하시오. 기약분수란 더 이상 약분되지 않는 분수를 의미한다.

 

[입력]

첫째 줄과 둘째 줄에, 각 분수의 분자와 분모를 뜻하는 두 개의 자연수가 순서대로 주어진다. 입력되는 네 자연수는 모두 30,000 이하이다.

[출력]

첫째 줄에 구하고자 하는 기약분수의 분자와 분모를 뜻하는 두 개의 자연수를 빈 칸을 사이에 두고 순서대로 출력한다.

 

[예제]

 


>문제 풀이

input))

분자1 분모1

분자2 분모2

이렇게 입력되면

c1/p1 과 c2/p2를 더하고 기약분수를 구해주면 됩니다.

나올 수 있는 최대수는 입력되는 분자 또는 분모가 30,000이하 이므로 9억 int 범위로 감당 가능합니다.

1) 분수의 합을 구하고

2) gcd를 구하여 기약분수를 만들어 출력한다.

gcd를 구할 때는 호제법을 사용한다.

> 유클리드 호제법이란?

gcd(A, B)= gcd(B, A%B)

* 증명

G를 A와 B의 최대공약수라고 할 때, A= aG, B= bG

A= Bq+r 니까

aG= bGq + r

r중심으로 정리하면 r = aG - bGq = (a-bq)G

B= bG , r= (a-bq)G 니까 G가 B와 r의 최대 공약수가 되려면 b와 (a-bq)가 서로소 임을 증명해야한다.

만약 서로소가 아니라고 가정을 해보자.

두 수가 서로소가 아니라면 b= mg , a-bq= ng 로 g라는 공약수가 존재한다.

b의 값을 오른쪽 식에 대입하면 a-mgq=ng

정리하면 a= (mq+n)g 이다.

우리는 처음에 A와 B 두 수를 A= aG, B= bG (G는 A와 B의 최대공약수) 라고 선언했는데, 즉 a와 b는 서로소라는 의미이다.

근데 아래 식에서 b= mg 이고 a= (mq+n)g 이면 a와 b는 공약수 g를 갖게된다.

따라서 b와 (a-bq)는 서로소이다.

 

>전체 코드

 

import java.util.Scanner;

public class Main {
	
	public static void main(String[] args) {
		Scanner scan= new Scanner(System.in);
		int c1, c2, p1, p2;
		
		//c1/p1, c2/p2
		c1= scan.nextInt();
		p1= scan.nextInt();
		c2= scan.nextInt();
		p2= scan.nextInt();
		
        //더해주고
		c1=c1*p2+c2*p1;
		p1=p1*p2;
		int gcd= getGcd(c1, p1); //최대공약수를 구해준다.
		
		System.out.println(c1/gcd+" "+p1/gcd); //기약분수 출력
	}//main
	
	public static int getGcd(int a, int b) {
		if(a%b==0) {
			return b;
		}
		return getGcd(b, a%b);
	}
}

https://www.acmicpc.net/problem/1735

 

1735번: 분수 합

첫째 줄과 둘째 줄에, 각 분수의 분자와 분모를 뜻하는 두 개의 자연수가 순서대로 주어진다. 입력되는 네 자연수는 모두 30,000 이하이다.

www.acmicpc.net

 

[문제]

창영이는 선영이가 사탕을 공평하게 나누어주지 않으면 친구들을 때릴정도로 사탕을 좋아한다.

따라서, 선영이는 다음 파티에 사용할 사탕을 구매하기 전에 고민을 하기 시작했다.

만약 파티에 K명이 참가한다면, 공정하게 나누어주려면 K×X개를 사야 한다. (X는 자연수)

선영이는 항상 적어도 한 아이는 사탕을 잃어버린다는 사실을 알고 있다. 그래서 캔디를 하나 더 구매해 총 (K×X+1)개를 구매하려고 한다.

사탕은 봉지 단위로 판매한다. 한 봉지에는 사탕이 총 C개 들어있다. 문제의 조건을 만족하면서 구매할 수 있는 사탕 봉지의 개수를 구하는 프로그램을 작성하시오.

 

[입력]

첫째 줄에 테스트 케이스의 개수 t가 주어진다. (0 < t < 100) 각 테스트 케이스는 한 줄로 이루어져 있고, K와 C가 공백으로 구분되어져서 주어진다. (1 ≤ K, C ≤ 109) 선영이는 부자가 아니기 때문에 109개를 넘는 사탕 봉지를 구매하지 못한다.

 

[출력]

각 테스트 케이스에 대해서 문제의 조건을 만족시키면서 구매할 수 있는 사탕 봉지가 없다면, "IMPOSSIBLE"을 출력한다. 이 경우가 아닌 경우에는 선영이가 구매해야 하는 사탕 봉지의 수를 출력한다. 만약, 가능한 봉지의 수가 여러개라면 아무거나 출력한다.

 

[예제]


>문제 풀이

창영이는 사탕을 좋아한다.

선영이는 구매하기 전에 고민을 시작했다.

만약, K명이 참가한다면, 공정하게 K x X(자연수) 사야한다.

항상 한 아이는 사탕을 잃어버린다.

그래서 (K x X + 1) 개를 구매해야한다.

사탕은 봉지 단위로 판매한다.

사탕은 총 C개 들어있다.

몇 봉지를 사야하는가?

input))

t     //(0<t<100)

K C  //(1<=K, C<=10^9)

output))

만족하는 사탕 봉지가 없다면, "IMPOSSIBLE"

여러개면 아무거나 출력

즉, K와 C가 주어지고 x: 인당 나눠줄 사탕 개수, y: 구매할 사탕 봉지 개수 라고 할때,

-Kx+Cy=1 을 만족하는 y를 찾아야한다.

* X의 시작점은 K/C의 올림수=> (int)(K+1)/C

>문제 풀이

gcd를 구할 때 유클리드 호제법을 쓴다면, 일차방정식의 해를 구할 때는 확장된 유클리드 호제법을 사용할 수 있다.

확장된 유클리드 호제법 설명 참고: https://sexycoder.tistory.com/65

 

유클리드 호제법과 그 확장에 대한 증명

유클리드 호제법의 증명 유클리드 호제법이란 큰 값에 대한 GCD(최대공약수)를 구하는 알고리즘을 말한다. 2개의 자연수 A,B가 있고 A%B=r이라고 했을 때 다음을 만족한다. 위 식을 증명하기 위해선

sexycoder.tistory.com

 

>전체 코드

 

import java.util.Scanner;

public class Main {
	
	public static void main(String[] args) {
		int N, K, C;
		Scanner scan= new Scanner(System.in);
		
		N= scan.nextInt();
		for(int i=0; i<N; i++) {
			K= scan.nextInt();
			C= scan.nextInt();
			long x0, y0;
			
			EGResult result= extendedGcd(K, C);
			if(result.r!=1) {
				System.out.println("IMPOSSIBLE");
			}else {
				x0= result.s;
				y0= result.t;
				
				y0%=K;
				if(y0<0) y0+=K;
				y0= Math.max(y0, (K+C)/C);
				if(y0<= 1e9) {
					System.out.println(y0);
				}else {
					System.out.println("IMPOSSIBLE");
				}
			}
		}
	}//main

	public static EGResult extendedGcd(long a, long b) {
		long s0=1, t0=0, r0=a;
		long s1=0, t1=1, r1=b;
		
		long temp;
		while(r1!=0) {
			long q= r0/r1;
			
			temp= r0 - q*r1;
			r0= r1;
			r1= temp;
			
			temp= s0 - q*s1;
			s0= s1;
			s1= temp;
			
			temp= t0 - q*t1;
			t0= t1;
			t1= temp;
		}
		return new EGResult(s0, t0, r0);
	}//EGResult
	
	static class EGResult{
		long s, t, r;
		public EGResult(long s, long t, long r) {
			super();
			this.s= s;
			this.t= t;
			this.r= r;
		}
	}//EGResult
}

https://www.acmicpc.net/problem/3955

 

3955번: 캔디 분배

각 테스트 케이스에 대해서 문제의 조건을 만족시키면서 구매할 수 있는 사탕 봉지가 없다면, "IMPOSSIBLE"을 출력한다. 이 경우가 아닌 경우에는 선영이가 구매해야 하는 사탕 봉지의 수를 출력한

www.acmicpc.net

 

+ Recent posts