본문 바로가기

예전글/BOJ 문제 풀어보기

[백준][알고리즘][15629번] Africa (Java) : 문제 분석



 * 오류 지적, 오타, 내용 개선 댓글은 대환영입니다!



 안녕하세요, 갓벨입니다. 이번 문제는 15629번, Africa입니다.


 BOJ 사이트를 돌아 보다가, 스크롤을 맨 아래로 내리는 순간 유저 대회인 '제 1회 구데기컵'이라는 대회가 눈에 띄었습니다. 이름이 너무 강렬해서 클릭해 보니 문제들이 꽤 있었습니다. 호기심에 첫 번째 문제인 Africa를 클릭해 보니 문제가 장난 아니게 길어서 당황했네요;


 문제에서 힌트와 조건만 잘 찾아 내서 풀면 되는 문제였습니다. 숨어 있는 조건을 찾지 못하고 그냥 푼다면 틀리기 일쑤입니다. 이번 문제는 길어서 더보기로 넣겠습니다. 그리고 원래 문제에는 없지만 풀이를 위해 단락마다 번호를 넣겠습니다.


문제



입력

 첫 줄의 입력 데이터의 개수 N (1 ≤ N ≤ 8)이 입력된다.

 다음 N줄에 걸쳐 한 줄에 하나씩 단어가 입력되며, 입력되는 단어는 다음의 8개 중 하나이며, 같은 단어가 두 번 이상 입력되는 경우는 없다. 

 botswana, ethiopia, kenya, namibia, south-africa, tanzania, zambia, zimbabwe


출력

 문제에 해당하는 답을 출력한다. 문제 해결에 필요한 모든 정보는 이 문제에서 제시되는 것을 기준으로 한다.

예제 입력

3
ethiopia
kenya
tanzania
 * 예제는 하나만 적겠습니다.

예제 출력

150

답안


import java.util.Scanner; class Main { public static void main(String args[]) { Scanner sc = new Scanner(System.in); int N = sc.nextInt(); int cost = 0; boolean SAexists = false; boolean ZIexists = false; boolean ZAexists = false; String plan; for(int i = 0 ; i < N ; i++) { plan = sc.next(); switch (plan) { case "botswana" : cost += 0; break; case "ethiopia" : cost += 50; break; case "kenya" : cost += 50; break; case "namibia" : if(SAexists) cost += 40; else cost += 140; //대행사 $140 break; case "south-africa" : SAexists = true; cost += 0; break; case "tanzania" : cost += 50; break; case "zambia" : ZAexists = true; if(ZIexists) cost += 20 + 0; else cost += 50; break; case "zimbabwe" : ZIexists = true; if(ZAexists) cost += 0; else cost += 30; break; } if(!plan.equals("zambia") && !plan.equals("zimbabwe")) { ZAexists = false; ZIexists = false; } } System.out.println(cost); } }


풀이

 문제가 깁니다. 그렇다고 지레 겁먹고 못 풀 문제는 아닙니다. 
 입력 예제를 보니 여행 경로 같습니다. 출력은 걸리는 시간일 수도 있고, 드는 비용일 수도 있고, 아직까지는 확실치 않네요. 혹시 모르니 숫자가 나올 때마다 메모해 두기로 합시다. 하지만 한 단락씩 읽다 보면 문제와 문제의 조건, 힌트 등이 모두 쉽게 드러나기 때문에, 차근차근 읽는다면 쉽게 해결할 수 있습니다. 문단별로 어떤 힌트가 있는지 알아 봅시다.

 1) 2) 3) 딱히 힌트는 없는 것 같습니다.

 4) "대행사를 통해 비자를 받는 경우 필요한 비용은 140달러로 매우 비싸기 때문에 현지에서 비자를 받는 것이 비용 측면에서는 유리하지만, 아프리카에서 한국인이 나미비아 비자를 받을 수 있는 곳은 매우 한정적이다."와  "많은 여행객들이 가는 지역 중에서는 남아프리카공화국의 케이프타운이 사실상 유일하기 때문에 경로 상 남아프리카공화국을 먼저 방문하지 않거나 케이프타운에서 비자가 발급될 때까지 충분히 머무를 수 없는 사람의 경우 한국에서 비자를 신청해야 한다. 만약 남아프리카공화국에서 비자를 받는 것이 가능하다면 그 비용은 40달러로 매우 저렴해진다." 이 두 문장에서 힌트를 알 수 있습니다. 여기부터 뭔가 힌트가 나오네요. 뭘 구해야 하는지는 아직 모르겠지만, 일단 적어둡시다. "나미비아를 갈 때 남아공을 거쳐 가면 $40, 바로 가면 $140"

 5) "예를 들어 케냐에 입국하려면 50달러를 내고 1회 입국이 가능한 비자를 발급받거나, 100달러를 내고 여러 번 입국이 가능한 비자를 받아야 한다."
 이것도 적어둡시다. 아무래도 문제에서 구해야 하는 게 비용일 것 같네요. "케냐 1회 입국 비자는 $50, 여러 번 입국 가능 비자는 $100"

 6) "남아프리카공화국은 무비자 입국으로 30일 체류가 가능하며, 보츠와나는 90일 체류가 가능하다."
 두 개의 국가가 한 번에 나오네요. "남아공은 무비자 30일, 보츠와나는 무비자 90일"

 7) 마운과 오카방고 델타에 관한 설명 뿐입니다. 1박 2일 프로그램 얘기가 있는데 당일치기도 있다고 하니까 이건 힌트가 아니겠네요.

 8) 역시 오카방고 델타에 대한 설명입니다. 별다른 힌트는 없습니다.

 9) "짐바브웨의 경우 1회 입국 가능한 비자의 발급 비용이 30불, 2회 입국 가능한 비자는 45불이다. 잠비아는 1회 입국 가능한 비자가 50불, 2회 입국 가능한 비자가 80불이다.", " 다행히 이와 같이 방문하는 여행객들이 선택할 수 있는 옵션이 한 가지가 더 있는데, 잠비아-짐바브웨 연합 비자가 이에 해당한다. 이 비자를 발급받을 경우 지정된 기간 동안 짐바브웨와 잠비아 간을 제한 없이 방문할 수 있다. 단, 잠비아 혹은 짐바브웨가 아닌 다른 국가로 이동한 경우 비자는 유효하지 않게 된다. 이 비자를 발급받는 비용은 50불로, 두 국가를 모두 방문할 계획이 있는 여행객이라면 이를 발급받는 것이 유리하다.", "따라서 국가별 방문 순서를 잘 정하면 비자 발급 비용을 절약할 수 있다. 그래서 국가를 방문하는 순서가 있을 때 총 비자 발급 비용을 구하려고 한다. 지금까지의 설명과 앞으로 적을 다른 국가 및 지역에 대한 소개를 조합하면 이를 해결하기 위한 충분한 정보를 얻을 수 있을 것이다. 이때 비용을 줄이기 위해 정해진 순서 중간에 다른 국가를 방문하는 것도 생각해볼 수 있겠지만 일단 여기에서는 생각하지 않기로 하자."
 많은 힌트가 있는 단락입니다. 게다가 문제에서 뭘 구해야 하는지도 나와 있습니다. 총 비자 발급 비용을 구해야 하고, 언급되지 않은 국가는 거쳐가지 않는다고 합니다. 또 짐바브웨와 잠비아로 가는 비자의 발급 비용도 있네요. "짐바브웨 1회 입국 가능한 비자는 $30, 2회 입국 가능한 비자는 $45, 잠비아 1회 입국 가능한 비자는 $50, 2회 입국 가능한 비자는 $80, 짐바브웨에서 잠비아, 잠비아에서 짐바브웨로 이동할 때는 한 번만 $50, 중간에 다른 나라로 가면 무효"
 짐바브웨에서 잠비아로 갈 때 50달러 한 번만 내는 걸로 처리하려면 짐바브웨 다음에 잠비아로 갈 때 20달러만 더하면 되겠군요.

10) - 15) 비자 발급 비용에 관한 내용은 없습니다.

16) "에티오피아 역시 50달러를 내고 도착비자 발급이 가능하다. "
 에티오피아까지 나왔군요. 다음 단락에도 달리 힌트는 없으니 바로 문제를 풀면 되겠습니다.

 다음 표는 현재까지 나온 비자 발급 비용들을 적어 둔 것입니다. 문제에서 같은 국가를 여러 번 방문하지 않는다고 언급했기 때문에 2회 이상 방문할 때 필요한 비자 발급 비용은 적지 않겠습니다.

국가명

비자 발급 비용

비고

 나미비아

$140 ( $40 )

( ) : 남아공을 거쳤을 경우

 케냐

$50 

 

 남아공

$0

무비자 30일

보츠와나

$0

무비자 90일

짐바브웨

$30 ( $0 )

( ) : 이전 국가가 잠비아

잠비아

$50 ( $20 )

( ) : 이전 국가가 짐바브웨

탄자니아

$50


에티오피아

$50

 도착비자


 for문으로 N번 반복하면서 switch문에 각 국가마다 정수 변수 하나에 비용을 더하기로 합시다. 

        for(int i = 0 ; i < N ; i++)
        {
            plan = sc.next();
            switch (plan)
            {
                case "botswana" : //보츠와나
                    cost += 0; //무비자 90일
                    break;

                case "ethiopia" : //에티오피아
                    cost += 50; //도착비자 $50
                    break;

                case "kenya" : //케냐
                    cost += 50; //1회 입국 $50
                    break;

                case "namibia" : //나미비아
                    cost += 140; //대행사 $140
                    break;

                case "south-africa" : //남아공
                    cost += 0; //무비자 30일
                    break;

                case "tanzania" : //탄자니아
                    cost += 50; //1회 입국 $50
                    break;

                case "zambia" : //잠비아
                    cost += 50; //1회 입국 $50
                    break;

                case "zimbabwe" : //짐바브웨
                    else cost += 30; //1회 입국 $30
                    break;
            }

        }

추가 조건이 없었다면 그냥 이대로 for문과 switch문을 쓰면 되겠지만, 나미비아랑 짐바브웨, 잠비아 때문에 뭔가를 더 해야겠습니다. 저는 boolean형 변수를 이용하는 것으로 해결했습니다.


 남아공에 간 적이 있다면 나미비아를 갈 때 필요한 비자를 발급할 수 있기 때문에 남아공에 들른 적이 있기만 하다면 나미비아에 가는 비용은 $40입니다. 변수 하나(위에서는 SAexists)를 false로 선언해 남아공에 간 적이 있을 때 true가 되도록 설정합시다. 이렇게 하면 이후에 나미비아를 입력하면 40을 더합니다.

boolean SAexists = false;

case "south-africa" : // switch문 안 SAexists = true; cost += 0; break;

 짐바브웨랑 잠비아가 좀 복잡할 수 있는데요, 짐바브웨에서 잠비아로 갈 때는 $20달러만 더 내는 걸로 처리하면 되겠고, 잠비아에서 짐바브웨로 갈 때 짐바브웨에서는 비자 발급 비용을 안 더해도 되겠네요. 이렇게 하면 딱 $50

낸 게 됩니다. 짐바브웨나 잠비아를 방문하면 변수 하나씩(위에서는 각각 ZIexists와 ZAexists) 그리고 짐바브웨나 잠비아로 갔다가 다른 나라로 갔을 경우에는 이 비자를 사용할 수 없기 때문에 다른 나라를 방문했을 때는 이 두 변수를 모두 false로 만들어 주는 작업이 필요합니다.

case "zambia" : // switch문 안
    cost += 50;
    break;

case "zimbabwe" :
    else cost += 30;
    break;
if(!plan.equals("zambia") && !plan.equals("zimbabwe"))
{
    ZAexists = false;
    ZIexists = false;
}
//for문 안, switch문 다음

 이렇게 말이죠. 이렇게 되면 위 답안과 같이 되어 있습니다.


 단계별로 풀어보기 말고 다른 문제는 꽤 오랜만에 풀어보네요. 이렇게 긴 문제에서 힌트를 찾아 푸는 것도 재미있는 것 같습니다. 그러면, 다음 글에서 봅시다!