문제 링크
https://www.acmicpc.net/problem/20546
20546번: 🐜 기적의 매매법 🐜
1월 14일 기준 준현이의 자산이 더 크다면 "BNP"를, 성민이의 자산이 더 크다면 "TIMING"을 출력한다. 둘의 자산이 같다면 "SAMESAME"을 출력한다. 모든 결과 따옴표를 제외하고 출력한다.
www.acmicpc.net
풀기 전에 아이디어
기적의 매매법 문제는 준현이와 성민이의 각 매매법인 BNP, Timing 중 어떤 방법이 더 수익률이 높은 지를 따져보는 문제였다.
주식은 하나로 정해져있고, 현금과 14일 동안의 주가를 입력받아서 더 수익이 높은 매매법을 출력하고 같으면 같다고 출력하면 된다.
문제가 상당히 길어서 난독증 와가지고 하.. 이게 뭔 소린지 이해하는지 좀 걸렸다. ㅋㅋㅋㅋ (책을 많이 읽자..)
하여튼 이 문제에서 좀 유심히 봐야하는 부분이 각 매매법이 어떤 식으로 매매를 할 것인가였다.
준현이의 매매법인 BNP 방법은 자신이 보유한 현금으로 주식을 살 수 있을 때 최대한 다 사는 것이다. 첫 날부터 살 수 있으면 살 수 있는 만큼 다 사고, 다음 날에도 살 수 있을 만큼 현금을 보유하고 있으면 풀 매수를 하면 된다. 어쨌든 그냥 무지성 풀 매수 방법! 그리고 무조건 마지막 날인 14일에 풀매도!
성민이의 매매법인 Timing 방법이 조금 까다로웠다. 얘도 풀 매수 풀 매도 하는 방법인데, BNP와 다른 점은 살 수 있을 때 다 사는게 아니라 1~14일 주가를 보면 3일 연속 가격이 상승하면 다음날 무조건 가격이 하락한다고 가정한다. 반대로 3일 연속 가격이 하락하면 다음날 무조건 가격이 상승한다고 가정한다. 이런 타이밍에서 풀 매도, 풀 매수를 하게 된다. 전일 대비 가격이 같다면 가격이 상승하거나 하락하는 것은 아니라는 것도 포인트였다.
둘의 자산을 비교해야 하는데 자산은 (현금 + 1월 14일의 주가 * 주식 수)로 계산하면 된다.
과정
자 이제 늘 하던대로 BufferReader, StringTokenizer를 통해 현금과 14일 동안의 주가를 입력받는다. 주가는 배열에 저장하도록 했다.
이제 BNP 방법과 Timing 방법의 각 수익률을 계산하는 로직을 짜도록 한다.
<BNP 방법>
현준이의 BNP 방법은 살 수 있을 때 다 사야하기 때문에 배열에 담긴 주가를 순차적으로 확인하면서 내가 가진 현금보다 주가가 작거나 같을 때 풀 매수를 한다.
- for문과 if문으로 14일동안 내가 가장 빠르게 최대한 많이 살 수 있도록 환경을 마련해 준다.
- 임시변수 temp에 내가 구매한 주식 수를 계산하여 담아둔다.
- 내가 주식을 사면서 쓴 돈을 보유한 현금에서 빼주면서 현재 보유한 현금으로 업데이트해준다.
- 주식 개수를 담는 변수인 stock에 내가 방금 구매한 주식 수를 더해준다.
- 위에 적은 자산 계산 법인 (현금 + 1월 14일의 주가 * 주식 수) 대로 계산해 준다.
이렇게 해주면 현준이는 자기가 살 수 있는 조건이 만족된다면 무지성 풀 매수를 하게 된다!
<Timing 방법>
다음으로 성민이의 Timing 방법은 고민이 참 많았다. 3일 동안 하락장일 때 풀 매수를 해야하는데 3일동안 하락하고 있는지 체크를 어떻게 해야 할지 이게 참 고민이 많고 많았다.. 처음에는 날짜를 계산할만한 변수를 하나 만들어서 전 날의 가격을 저장해 두고 다음 날의 가격과 비교하면서 체크했다.
이게 나중 돼서 보니까 가격이 3일 하락장이고 나서 다음날 가격이 상승하긴 하는데 상승한 다음 날 전일 대비 가격이 똑같을 수도 있고 상승할 수도 있고 하락할 수도 있고 그래서 뭔가 체크하기 상당히 까다로웠다. 또 3일 동안 상승하는 거 체크하는 변수 만들어서 하니까 아 머리가 터져버릴 것 같고 이상했다.
그래서 3-4시간 미련하게 붙잡고 있다가 포기했다. ㅋㅋㅋㅋㅋㅋㅋㅋㅋ 난 멍청해서 못 풀 거야ㅜ 하고 그냥 잤음. 자고 일어나서 아무고토 하기 싫다가 하.. 그래도 자면서 리프레쉬했으니까 내가 짠 로직을 다시 봤다. 근데 뭔가 굉장히 간단한 방법이 생각났다.
price[i] < price[i - 1] && price[i - 1] < price[i - 2] && price[i - 2] < price[i - 3]
이렇게 and 연산자로 3일 동안 값이 하락하고 있는 걸 체크하면 참 쉽지 않나! 하고 바로 코드 갈아엎기.. ㅋㅋㅋ 사실 저 위의 방법은 누군가 구현했을 수 있다. 내가 바보라서 못 한걸 수도 있다. 근데 더 이게 가독성도 좋고 더 간결하고!! 괜찮지 않을까 해서 사용한 거다! (변명 아님)
근데 이렇게 하니까 뭔가 또 그 예제 출력에 쓰여있는 성민의 자산과 일치하게 나오지 않았다. 자산이 마이너스였다ㅋㅋㅋㅋ
얘는 돈을 다 잃었노ㅋ
말도 안 돼!!!! 빚 안 낸다며!!! 그래서 마이너스면 조건문이 이상한 거겠지 하고 아차 싶었음.
위 조건만 달 것이 아니라 내가 돈이 없을 수도 있잖아 ㅋㅋ 그거를 체크해줘야 했음 ㅜ
3일 동안 값이 오르고 있는 경우에는 반대로 조건을 달고 나서 로직을 완성했는데, 또 이상함. 아 이거 3일 동안 값 오를 때도 조건 하나 추가해줘야 하는데 언제 해야 하지? 생각해 봤는데 내가 가진 주식이 없는데, ㅋㅋㅋ주식을 팔고 있었음ㅜㅜ
그래서 이 때는 내가 가진 주식이 하나라도 있는지 체크해 주도록 했다. 자! 이제 끝일 걸~
- for문으로 3일 동안 하락, 상승하는 기간을 체크할 수 있도록 환경을 마련해 준다.
- 두 개의 if문으로 내가 돈을 가지고 있고, 값이 3일 동안 연속으로 하락하는 다음 날에 풀 매수 해준다.
- 내가 주식을 보유하고 있고, 값이 3일동안 연속으로 상승하는 다음날에 풀 매도 해준다.
- 풀 매수 방법은 현준이 방법과 같다.
- 풀 매도할 때는 내가 가진 주식 수와 현재 주가를 곱해서 수익을 계산한 후 현금에 더해주면서 업데이트해 준다.
- 내가 보유한 주식을 다 팔았을 태니, 보유 주식 수를 0개로 업데이트해준다.
- 자산 계산법 대로 계산해 준다.
자 이제 완벽하다. -> 구라임
(사실 완벽하지 않았다.) 코드가 너무 더러웠기 때문이다.
흠.. 원래 귀찮아서 잘 안 했는데 이미 제출이 많이 늦어서 예쁘게 다시 코드 정리해 줬음!
함수 호출 방식으로 바꿨다. 이게 뭔가 더 깔끔했다. 위에 설명한 대로 코드를 작성하고 bnp, timing 함수로 분리해주었다.
BNP 방법대로 낸 수익률을 준현이의 자산인 jMoney에 담아주고, Timing 방법대로 낸 수익률은 성민이의 자신인 sMoney에 담아주었다.
마지막으로 비교 연산 결과에 따른 출력문으로 구성하여 어떤 것이 수익이 더 짭짤한 방법인지 출력하도록 했다!!!!!!!
진짜 너무 힘들었다..... 힝..
시간 복잡도
반복문이 있지만 14번으로 고정돼서 반복하기 때문에 O(1)이라고 생각함!
전체코드
import java.io.*;
import java.util.*;
public class 20546 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int money = Integer.parseInt(br.readLine());
int[] price = new int[14];
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
for (int i = 0; i < 14; i++) {
price[i] = Integer.parseInt(st.nextToken());
}
int jMoney = bnp(money, price);
int sMoney = timing(money, price);
if (jMoney > sMoney) {
System.out.println("BNP");
} else if (jMoney < sMoney) {
System.out.println("TIMING");
} else {
System.out.println("SAMESAME");
}
}
private static int bnp(int money, int[] price) {
int stock = 0;
for (int i = 0; i < 14; i++) {
if (price[i] <= money) {
int temp = money / price[i];
money -= temp * price[i];
stock += temp;
}
}
return money + (stock * price[13]);
}
private static int timing(int money, int[] price) {
int stock = 0;
for (int i = 3; i < 14; i++) {
if (money > 0 && price[i] < price[i - 1] && price[i - 1] < price[i - 2] && price[i - 2] < price[i - 3]) {
int temp = money / price[i];
money -= temp * price[i];
stock += temp;
}
if (stock > 0 && price[i - 3] < price[i - 2] && price[i - 2] < price[i - 1] && price[i - 1] < price[i]) {
money += stock * price[i];
stock = 0;
}
}
return money + (stock * price[13]);
}
}