BOJ 14501. 퇴사

polynomeer·2020년 4월 13일
0

Baekjoon Online Judge

목록 보기
15/20
post-thumbnail

BOJ 14501. 퇴사

상담원으로 일하고 있는 백준이는 퇴사를 하려고 한다.
오늘부터 N+1일째 되는 날 퇴사를 하기 위해서, 남은 N일 동안 최대한 많은 상담을 하려고 한다.
백준이는 비서에게 최대한 많은 상담을 잡으라고 부탁을 했고, 비서는 하루에 하나씩 서로 다른 사람의 상담을 잡아놓았다.
각각의 상담은 상담을 완료하는데 걸리는 기간 Ti와 상담을 했을 때 받을 수 있는 금액 Pi로 이루어져 있다.
N = 7인 경우에 다음과 같은 상담 일정표를 보자.

1일2일3일4일5일6일7일
Ti3511242
Pi102010201540200

1일에 잡혀있는 상담은 총 3일이 걸리며, 상담했을 때 받을 수 있는 금액은 10이다. 5일에 잡혀있는 상담은 총 2일이 걸리며, 받을 수 있는 금액은 15이다.

상담을 하는데 필요한 기간은 1일보다 클 수 있기 때문에, 모든 상담을 할 수는 없다. 예를 들어서 1일에 상담을 하게 되면, 2일, 3일에 있는 상담은 할 수 없게 된다. 2일에 있는 상담을 하게 되면, 3, 4, 5, 6일에 잡혀있는 상담은 할 수 없다.

또한, N+1일째에는 회사에 없기 때문에, 6, 7일에 있는 상담을 할 수 없다.
퇴사 전에 할 수 있는 상담의 최대 이익은 1일, 4일, 5일에 있는 상담을 하는 것이며, 이때의 이익은 10+20+15=45이다.
상담을 적절히 했을 때, 백준이가 얻을 수 있는 최대 수익을 구하는 프로그램을 작성하시오.

1. 문제 해석

• N+1일이 되는 날 퇴사를 하려고 한다(1≤N≤15)
• 남은 N일동안 최대한 많은 상담을 하려고 한다
• 하루에 하나의 상담을 할 수 있고
• i일에 상담을 하면,T[i]일이 걸리고 P[i]원을 번다

좀 극단적인 예를 들어보면 N=5일때 다음과 같다.

1일2일3일4일5일6일
Ti3122100퇴사
Pi253310억-
상담oooooo-
상담xxxxxx-

이 경우에 만일 5일의 상담을 한다면 10억을 벌 수 있다. 하지만 6일째 되는 날 퇴사를 해야하므로, 5일에 상담을 하는 것은 불가능하다. 이런 문제는 '어떻게해야 최대 수익을 낼 수 있을까?'로 접근하면 다소 어려울 수 있다. 가장 간단한 해결 방법은 '어떻게해야 모든 경우를 구해볼 수 있을까?'로 접근해서 모든 경우마다 총 상담비용을 구해서 최종적으로 최댓값을 구하면 될 것이다.

상담을 하거나, 하지 않는 경우로 나누어서 각 날짜마다 선택한다면 2^N의 경우가 생긴다. 따라서 시간복잡도는 O(2^N)이므로 충분히 풀 수 있다.



2. 문제 풀이

N+1일이 되면 퇴사하게 되므로 day를 기준으로 재귀호출의 종료조건을 걸면 될 것이다. 또한, 상담비용을 기록하기 위해서 sum에 그 값을 누적한다.

  • go(day,sum)
    • day일이 되었다. day일에 있는 상담을 할지 말지 결정해야 한다.
    • 지금까지 얻은 수익은 sum이다.

day를 계속 증가시키면서 정답을 찾거나 불가능한 경우를 제외할 수 있다. 그리고 정답을 찾았다면 그때의 누적된 수익을 sum을 통해 반환할 수 있다. 이 두가지 경우가 아니라면 다음 경우를 실행하기 위해 재귀호출을 해야하는데, 이때에는 두 가지 경우가 있다. 해당 day의 상담을 하거나, 하지 않는 경우이다.

상담을 하지 않는 경우에는 그냥 day+1을 하여 재귀호출 하면된다. 상담을 하는 경우에는 어차피 해당 상담일만큼 다른 상담을 할 수 없으므로, 그냥 그 상담일 만큼 day에 더해준다.

  • 정답을 찾은 경우
    • day == n

  • 불가능한 경우
    • day > n

  • 다음 경우
    • 상담을 한다 : go(day+t[day], sum+p[day])
    • 상담을 하지 않는다 : go(day+1, sum)

#include <iostream>
using namespace std;

int T[21], P[21], N;
int ans = 0;

void go(int day, int sum) {	// day일에 sum만큼의 수익
    if (day == N+1) {		// N+1일이 되면 정답을 찾을 경우이므로 ans=sum후 종료
        if (ans < sum) ans = sum;
        return;
    }
    if (day > N+1) {		// N+1일 보다 커지면 불가능한 경우이므로 종료
        return;
    }
    go(day+1, sum);		// 상담을 하지 않고 하루가 지나는 경우
    go(day+T[day], sum+P[day]);	// 해당 상담을 하는 경우
}

int main() {
    cin >> N;
    for (int i=1; i<=N; i++) {
        cin >> T[i] >> P[i];
    }
    go(1, 0);
    cout << ans << '\n';
    return 0;
}
profile
어려운 문제를 어렵지 않게.

0개의 댓글