문제
상근이가 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이 음수이기 때문에, 상근이가 만들 수 없는 등식이다.
숫자가 주어졌을 때, 상근이가 만들 수 있는 올바른 등식의 수를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 숫자의 개수 N이 주어진다. (3 ≤ N ≤ 100) 둘째 줄에는 0 이상 9 이하의 정수 N개가 공백으로 구분해 주어진다.
출력
첫째 줄에 상근이가 만들 수 있는 올바른 등식의 개수를 출력한다. 이 값은 2^63-1 이하이다.
https://www.acmicpc.net/problem/5557
풀이과정
dp를 이용하여 문제를 해결했습니다.
처음에는 범위에 맞게 배열을 선언했고 그 뒤로 개수의 맞게 입력을 받았습니다. 참고로 마지막 수는 이 문제에서 목표로 하는 출력 값입니다. 이 값이 나오는 경우의 수를 구하는 것입니다.
목표의 값이 나오도록 경우의 수를 구하는 과정을 정리하자면
배열을 돌면서 두 수를 더하거나 빼거나 했을 때 그 수가 범위 (0 이상 20 이하) 안에 있을 경우 그 수의 경우의 수를 현재 수의 경우의 수에 더하는 식으로 문제를 해결했습니다. 그리고 끝에 인덱스 N - 2에 목표값에 도달한 경우의 수를 출력합니다.
코드
#include<iostream>
#include<vector>
#include<algorithm>
#define endl "\n"
using namespace std;
int arr[101];
long long dp[101][21];
void Answer()
{
int N , lastNumber;
cin >> N;
for (int i = 0; i < N - 1; i++)
{
cin >> arr[i];
}
cin >> lastNumber; // 마지막 수는 목표 수
dp[0][arr[0]] = 1; // 처음에 들어가는 기본 수
for (int i = 1; i < N - 1; i++) // N 에서 마지막 수를 제외한 길이
{
for (int j = 0; j < 21 ; j++)
{
if (dp[i - 1][j] == 0) continue; // 값이 없으면 넘기기
if (j + arr[i] <= 20) // + 했을 경우 20이 안 넘으면 경우의 수 더하기
{
dp[i][j + arr[i]] += dp[i - 1][j];
}
if (j - arr[i] >= 0) // - 했을 경우 0 보다 큰 경우의 수 더하기
{
dp[i][j - arr[i]] += dp[i - 1][j];
}
}
}
cout << dp[N - 2][lastNumber]; // 목표 수 의 경우의 수 출력
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
Answer();
return 0;
}