호우동의 개발일지

Today :

article thumbnail

문제 이해 단계

 

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

날짜 N과 각 날마다 할 수 있는 일
Ti와 그 일을 했을 때 버는 돈 Pi가 각각 주어진다.

Ti는 그 일을 할 때마다 드는 날을 뜻하며, 그 일을 하는 동안에는 다른 일을 할 수 없다.

이런 상황일 때 돈을 최대한 벌었을 때의 수익을 구하는 문제이다.

 


문제 접근 단계

표

이해부터가 살짝 복잡했는데,
N일까지 일을 해서 벌 수 있는 최대수익을 D [N]이라고 하자.

이때 유의해야 할 점은 N일까지라는 것은 N일을 포함하지 않는다.
즉 1~N-1일에 한 일만 포함하는 것이다.

이를 이용해서 1일부터 살펴보자면,
1일까지 일을 해서 번 돈은 당연히 0원일 것이고(D [1] = 0)

1일 때 일해서 번 돈은 3일이 지난 4일째의 받을 수 있다.
일단 이를 D [4] = 10이라도 해두자.

2일까지 일해서 벌 수 있는 최대 수익도 0원이다.(D [2] = 0)
그리고 2일에 일한 돈은 5일 뒤인 7일에 받을 수 있다(D [7] = 20)

이렇게 계속해서 가다가 4일째를 살펴보자.
4일째 받을 수 있는 돈은 크게 2가지가 있다.

1. 1일째에 일한 돈 10원
2. 3일째 일한 돈 10원 

만약 이 둘 중 더 큰 게 있다면 그걸 택하면 될 것이다. 
D [4] = Max(1일째 번돈, 3일째 번돈)

이런 식으로 N 전일까지 번 돈 D [N]을 구할 때, i번째 일을 할 경우를 비교해 본다.

1. 기존에 N일까지 일해서 벌 수 있는 돈의 총합
2. i일까지 벌 수 있는 돈의 총합 + i일에 일을 했을 때 벌 수 있는 돈

두 경우의 수 중 더 큰 값이 D [N]이 된다.

해당 문제는 말로 설명하는 것이 좀 복잡하기 때문에
코드를 보면서 설명하겠다.

 


문제 구현 단계

#include <iostream>
#include <utility>
using namespace std;

int N;
pair<int ,int> v[1500010];
int d[1500010];

int main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(false);

    cin >> N;

    for (int i = 1; i <= N; i++)
    {
        int v1, v2;
        cin >> v1;
        cin >> v2;
        v[i] = make_pair(v1, v2);
    }

    int cntMax = 0;
    for (int i = 1; i <= N+1; i++)
    {
        cntMax = max(cntMax, d[i]);
        if(i+v[i].first > N+1) continue;

        d[i + v[i].first] = max(cntMax + v[i].second,d[i + v[i].first]);
    }
    cout << cntMax;
}

입력이 걸리는 일수와 얻는 돈
이렇게 두 가지 정보로 들어오기 때문에 pair을 이용하여 값을 받았다.

그리고 cntMax라는 값을 활용하여, 나온 값들 중 가장 큰 값을 저장한다.

1부터 N+1까지 순차적으로 돌면서 탐색을 한다.

다음 일 까지는 (현재 순서 + 걸리는 일 수) 이기 때문에
i + v [i]. first를 인자로 활용하고, 이 값이 N+1이 넘어가는 것을 방지한다.

그리고 위에 설명했듯이, d [N]은 N전일까지 일했을 때의 최대 돈의 총합이다.
d [i + v [i]. first]는 d [N]를 뜻한다. 

max(cntMax + v [i]. second, d [i + v [i]. first]는
(현재 일수까지 일했을 때의 최댓값+현재일 수에서의 일값), (기존)을 의미한다.

그 두 값을 비교하여 더 큰 값으로 d [N]을 갱신한다.
이 과정을 반복하여 cntMax를 계속 갱신한다.

이 과정을 반복하여 cntMax를 출력한다.

 


시행착오

처음에는 DP를 활용하여 이중반복문을 활용하여 완전탐색으로 풀었다.
그런데 시간초과를 맛보고 여러 가지 방법을 생각해 봤는데 도저히 떠오르지 않았다.

그래서 구글링 해서 이 방법을 알았는데,
풀이를 여러 번 비교해 보고 구현해 보니까 이해가 되더라.

이해하는데 좀 오래 걸렸다.
이 방법은 혼자 생각하기 어려웠던 것 같다.

이런 식으로 DP를 구성하는 방식을 했던 적은 없었던 것 같다.

항상 기준에 되는 인덱스의 배열을 정하는 것만 해봤지,
그 인덱스를 통해 다른 배열의 DP를 정하는 행위는 해본 적이 없어서 이런 방식은 정말 새로웠다.
이해하는 것도 오래 걸렸다.

그래도 한번 해봤으니까 언뜻언뜻 떠올라서 앞으로는 이보다는 더 쉬울 것 같다.