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

 

11501번: 주식

입력의 첫 줄에는 테스트케이스 수를 나타내는 자연수 T가 주어진다. 각 테스트케이스 별로 첫 줄에는 날의 수를 나타내는 자연수 N(2 ≤ N ≤ 1,000,000)이 주어지고, 둘째 줄에는 날 별 주가를 나타

www.acmicpc.net

코드설명

그리디 알고리즘입니다.

처음에는 단순한 구현처럼 보이지만, 문제에 대한 아이디어가 있어야 풀 수 있습니다.

 

만약 앞에서부터 맨 뒤까지 최대값을 순회하여 구한다면 시간복잡도는 O(N^2) 이므로 시간초과가 납니다.

N의 조건은 자연수 N(2 ≤ N ≤ 1,000,000) 입니다.

 

만약 뒤에서부터 연산을 시작하면, O(N) 으로 시간복잡도를 변경시킬 수 있습니다.

 

예시를 들어보겠습니다.

1
5
1 1 3 1 2

1. [1 , 1 , 3 , 1 , 2] 에서 맨 뒤의 2부터 앞으로 이동시키며 진행시켜보겠습니다.

1-1.max  = 0, arr[4] = 2 이므로, max = 2 로 변경됩니다.

1-2 max = 2, arr[3] = 1 이므로, max > arr[3] 이기에  answer += 1

1-3.max =2, arr[2] = 3 이므로, max = 3 으로 변경됩니다.

1-4.max=3, arr[1] = 1 이므로, max > arr[1] 이기에 answer += 2 

1-5.max=3, arr[0] = 1 이므로, max > arr[0] 이기에 answer += 2 

answer = 5 가 나옵니다.

 

위의 로직을 확인해보면 O(N)의 시간복잡도임을 확인할 수 있습니다.

 

아래는 이 문제와 비슷한 로직의 문제입니다.

https://passionfruit200.tistory.com/5

 

[백준] 13305번 주유소 - 그리디(Greedy, 탐욕법) JAVA

https://www.acmicpc.net/problem/13305 13305번: 주유소표준 입력으로 다음 정보가 주어진다. 첫 번째 줄에는 도시의 개수를 나타내는 정수 N(2 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 인접한 두 도시를 연

passionfruit200.tistory.com

이 주유소 문제의 경우 앞에서부터 가장 효율적인 주유를 위해 처리하는 방식이고,

아래의 경우는 뒤에서부터 가장 효율적인 투자를 처리하는 방식입니다. (앞에서부터 처리할경우에는 불필요한 자료구조들이 필요합니다.)

연산의 순서를 바꾸는것만으로도 매우 빠르게 해결할 수 있습니다.

코드

뒤에서부터 확인하며, max값을 바로바로 확인하는 코드( 시간복잡도 O(N) )

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
public static int T, N;
public static int[] arr;
public static long answer = 0;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
T = Integer.parseInt(st.nextToken());
for(int i = 0;i < T; i++) {
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
answer = 0;
arr = new int[N];
st = new StringTokenizer(br.readLine());
for(int j=0;j<N;j++) {
arr[j] = Integer.parseInt(st.nextToken());
}
long maxValue = 0;
for(int j=N-1; j>=0;j--) {
if(maxValue < arr[j]) {
maxValue = arr[j];
}else {
answer += maxValue - arr[j];
}
}
System.out.println(answer);
}
}
}

 

정답코드 2 입니다.

package Main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class Main {
static int N, C, H, W, K, M, T;
static int[] arr;
static int answer = 0;
static StringBuilder sb = new StringBuilder();
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
T = Integer.parseInt(st.nextToken());
while(T-- > 0) {
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
arr = new int[N];
st = new StringTokenizer(br.readLine());
for(int i=0;i<N;i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
sb.append(stock()).append("\n");
}
System.out.println(sb.toString());
}
static long stock() {
long max = arr[N-1];
long profit = 0;
for(int i=N-1; i>=0; i--) {
if(arr[i] <= max) {
profit += max - arr[i];
}else if(arr[i] > max) {
max = arr[i];
}
}
return profit;
}
}

 

우선순위큐를 활용한 경우(여전히 시간초과가 발생한다)

시간복잡도는 우선순위큐는 삽입시에 log N 이고, N번 반복하니 N log N 입니다.

삭제시에는 log N 의 시간복잡도입니다.

총 시간복잡도는 O(T N log N ) 입니다. T = 개수가 안나와있음, N = 10^6 인데,

log_2(10^6) = 19.931568569324 입니다. (알고리즘 상에서 log의 밑은 일반적으로 2입니다. (10이 아님)).

여기서 T가 조금이라도 10^2 보다도 더 커진다면 바로 시간초과가 나오므로 시간초과가 발생하는 것 같습니다.

 

그리고 아래 코드 구현시에 해당하지 않는 pq값을 반드시 remove 해주어야 이후의 연산에 영향을 미치지 않습니다. ( 처음에 해당 부분 놓쳐서 반례가 틀렸습니다.)

package Main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class Main {
static int N, C, H, W, K, M, T;
static long[] arr;
static int answer = 0;
static PriorityQueue<Long> pq = new PriorityQueue<>(Collections.reverseOrder());
static StringBuilder sb = new StringBuilder();
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
T = Integer.parseInt(st.nextToken());
while(T-- > 0) {
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
arr = new long[N];
st = new StringTokenizer(br.readLine());
pq.clear();
for(int i=0;i<N;i++) {
arr[i] = Long.parseLong(st.nextToken());
pq.offer(arr[i]);
}
sb.append(stock()).append("\n");
}
System.out.println(sb.toString());
}
static long stock() {
long cost = 0; //구매하는데 사용한 금액
long cnt = 0;
long profit = 0; // 이익
for(int i=0;i<N; i++) {
cost += arr[i];
cnt += 1;
//만약, 가장 최대값과 같다면,
if(arr[i] == pq.peek()) {
profit += arr[i] * cnt - cost;
cost = 0;
cnt = 0;
}
pq.remove(arr[i]);
}
return profit;
}
}

 

시간복잡도 O(N^2) 인 코드 ( 시간초과 )

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
public static int T, N;
public static int[] arr;
public static int answer = 0;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
T = Integer.parseInt(st.nextToken());
for(int i = 0;i < T; i++) {
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
answer = 0;
arr = new int[N];
st = new StringTokenizer(br.readLine());
for(int j=0;j<N;j++) {
arr[j] = Integer.parseInt(st.nextToken());
}
for(int j=0;j<N;j++) {
int standard = arr[j];
int maxValue = 0;
for(int k=j + 1; k<N;k++) {
if(maxValue < arr[k] ) {
maxValue = arr[k];
}
}
if(maxValue > arr[j]) { //만약 최대값보다 작다면 해당 주식 구매하고 판매.
answer += maxValue - standard;
}
}
System.out.println(answer);
}
}
}

+ Recent posts