문제 설명
상근이가 1학년 때, 덧셈, 뺄셈을 매우 좋아했다. 상근이는 숫자가 줄 지어있는 것을 보기만 하면, 마지막 두 숫자 사이에 '='을 넣고, 나머지 숫자 사이에는 '+' 또는 '-'를 넣어 등식을 만들며 놀고 있다. 예를 들어, "8 3 2 4 8 7 2 4 0 8 8"에서 등식 "8+3-2-4+8-7-2-4-0+8=8"을 만들 수 있다.
상근이는 올바른 등식을 만들려고 한다. 상근이는 아직 학교에서 음수를 배우지 않았고, 20을 넘는 수는 모른다. 따라서, 왼쪽부터 계산할 때, 중간에 나오는 수가 모두 0 이상 20 이하이어야 한다. 예를 들어, "8+3+2-4-8-7+2+4+0+8=8"은 올바른 등식이지만, 8+3+2-4-8-7이 음수이기 때문에, 상근이가 만들 수 없는 등식이다.
숫자가 주어졌을 때, 상근이가 만들 수 있는 올바른 등식의 수를 구하는 프로그램을 작성하시오.
https://www.acmicpc.net/problem/5557
제한 사항
풀이
문제를 요약하면, 마지막 숫자를 제외한 숫자들로 +, - 를 이용해 마지막 숫자를 만드는 경우의 수를 구하는 것이다.
단, 모든 연산 과정 중 0 미만이거나 20을 초과하는 수를 사용해서는 안된다.
시간을 생각하지 않고 풀이를 해본다면 첫 숫자 혹은 마지막 숫자부터 +, -를 통해 연산 결과를 확인하며 완성할 수 있는지 확인하면 된다.
즉, dfs나 bfs로 확인이 가능하다.
하지만, $N <= 100$의 제한이 있기 때문에 $2^99$의 경우의 수를 확인해야 하기 때문에 시간초과가 발생할 것이다.
시간을 줄이기 위해서는 dp를 사용하면 된다.
모든 연산 결과를 유지하며 재귀를 진행하는 것이 아니라, i번째 숫자에서 만들 수 있는 수를 확인하면 된다.
예를 들어 보자.
11
8 3 2 4 8 7 2 4 0 8 8
우선, 가장 첫 수는 -를 할 수 없다.
0 미만으로 떨어지면 안 되기 때문이다.
따라서, 첫 번째 수로 만들 수 있는 수는 8 한 가지이다.
두 번째 수로 만들 수 있는 수는 5와 11 총 두 가지 이다.
- 8 - 3 = 5
- 8 + 3 = 11
세 번째 수로 만들 수 있는 수는 3, 7, 9, 13이다.
이렇게 N-1까지 계산을 하고 N-1번째 수에서 N번째 수인 8을 만들 수 있는 경우의 수를 출력하면 된다.
전체 코드
#include <bits/stdc++.h>
using namespace std;
int N;
vector<int> nums;
vector<vector<long long>> dp;
int ans = 0;
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cout << fixed;
cin >> N;
nums.resize(N);
for (int i = 0; i < N; i++)
{
cin >> nums[i];
}
dp.resize(N, vector<long long>(21, 0));
dp[0][nums[0]] = 1;
for (int i = 1; i < N-1; i++)
{
for (int j = 0; j <= 20; j++)
{
if (j - nums[i] >= 0)
{
dp[i][j - nums[i]] += dp[i - 1][j];
}
if (j + nums[i] <= 20)
{
dp[i][j + nums[i]] += dp[i - 1][j];
}
}
}
cout << dp[N-2][nums.back()];
return 0;
}