https://www.acmicpc.net/problem/5557
문제에 주어진 예시를 통해 풀이 방법을 고안해보자.
위의 그림처럼 N개의 원소에 대해 덧셈/뺄셈 연산을 반복적으로 수행하면, 시간복잡도는 O(2^N)이 될 것이다. N이 30까지만 커져도 약 10억 가량의 연산을 수행해야 하는 매우 비효율적인 방법이다.
따라서 덧셈/뺄셈 연산 결과를 별도의 테이블에 저장해두고 (메모이제이션), 다른 경우의 수에 대한 연산을 수행할 때 이미 계산된 결과가 있으면 테이블에 저장된 값을 그대로 사용하여 시간복잡도를 줄일 수 있다!
이처럼 dp 문제는 주어진 문제가 아래와 같은 조건을 만족시키는지 파악하는 것이 매우 중요하다!
- 최적 부분 구조 (Optimal substructure)
큰 문제를 작은 문제로 나눌 수 있으며, 작은 문제의 답을 모아서 큰 문제를 해결할 수 있다.- 중복되는 부분 문제 (Overlapping subproblem)
동일한 작은 문제를 반복적으로 해결한다.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <unordered_set>
#include <set>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N_MAX = 101;
const int V_MAX = 21;
ll dp[N_MAX][V_MAX];
ll arr[N_MAX];
int N;
void input() {
cin >> N;
for(int i = 0; i < N; i++){
cin >> arr[i];
}
}
ll dfs(int idx, int sum){
if(sum < 0 || sum > 20) return 0;
// 복사 연산 수행하지 않도록 reference 사용
ll& result = dp[idx][sum];
if(result != 0) return result;
if(idx == N - 2){
if(arr[N - 1] == sum){
return 1;
}
return 0;
}
result += dfs(idx + 1, sum + arr[idx + 1]);
result += dfs(idx + 1, sum - arr[idx + 1]);
return result;
}
void solution() {
cout << dfs(0, arr[0]);
}
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
input();
solution();
}