https://www.acmicpc.net/problem/2338
참고
- BOJ 10757 : 큰 수 A+B
- BOJ 10757 : 큰 수 A+B with 10^N진법
- BOJ 2338 : 긴자리 계산
- BOJ 2338 : 긴자리 계산 with 10^N진법
- 36진법 긴자리 두 수의 곱셈
- 36진법 긴자리 두 수의 곱셈 with 36^5진법
- 36진법 긴자리 두 수의 곱셈 with 36^5진법 + Fast Input
부호가 포함된 A, B의 덧셈, 뺄셈, 곱셈을 해보자.
10진수로 1,000자리가 주어지며, 곱셈 연산에 의해 최대 자릿수는 2,000이다.
큰 수의 정의
큰 수를 정의하기 위해 char 배열, 문자열의 길이, 그리고 부호를 가지는 구조체를 선언한다.
자릿수가 최대 1000이기 때문에 MAX를 2000으로 잡았다.
#define MAX (2010)
typedef struct st
{
char num[MAX];
int len;
int sign;
}BIG_NUMBER;
덧셈, 뺄셈, 곱셈을 구현해야 하는데, 이 문제는 부호가 있기 때문에 매우 까다롭다.
예를 들어 A - (-B)는 사실 A + B가 된다.
따라서 부호 처리는 나중에 하고,
각 함수는 부호를 모두 + 로 가정하고, 덧셈 함수는 덧셈만, 뺄셈 함수는 뺄셈만 한다.
이때, 뺄셈의 경우 문제가 발생한다.
A - B에서 B가 큰 경우 부호가 - 가 된다.
따라서 둘 중 누가 더 큰지 비교하는 함수를 하나 더 만들어, 반드시 큰 값에서 작은 값을 빼도록 한다.
따라서 구현해야 할 함수는 다음과 같다.
1. isBig : A > B라면 true
2. bigPlus : 같은 부호를 더하는 연산
3. bigMinus : 더 큰 수에서 작은 수를 빼는 연산
4. bigMultiply : 같은 부호를 곱하는 연산
isBig
BIG_NUMBER 구조체를 정의하였으므로, isBig은 다음과 같다.
문자열 길이가 더 큰 쪽이 값이 더 크고, 같은 문자열의 길이라면 하나씩 비교한다.
bool isBig(BIG_NUMBER& a, BIG_NUMBER& b)
{
if (a.len != b.len) return a.len > b.len;
for (int i = a.len - 1; i >= 0; i--)
{
if (a.num[i] != b.num[i]) return a.num[i] > b.num[i];
}
return true;
}
bigPlus
큰 수 A+B를 참고하자.
int로 변경한 후, 뒤집고, 다시 뒤집은 후, char로 변경하는 방법을 사용하므로 아래와 같이 구현하였다.
이 방법은 N진법 연산을 사용할 때 편하다.
BIG_NUMBER bigPlus(BIG_NUMBER& a, BIG_NUMBER& b)
{
BIG_NUMBER result = { 0 };
int lenA, lenB, len;
int temp[MAX] = { 0 };
lenA = a.len;
lenB = b.len;
len = (lenA > lenB) ? lenA : lenB;
for (int i = 0; i < lenA; i++) temp[i] += a.num[i];
for (int i = 0; i < lenB; i++) temp[i] += b.num[i];
for (int i = 0; i < len; i++)
{
temp[i + 1] += (temp[i] / 10);
temp[i] %= 10;
}
len++; // 자릿수가 오른 경우 체크
while (len && temp[len] == 0) len--;
len++;
for (int i = 0; i < len; i++) result.num[i] = temp[i];
result.len = len;
return result;
}
bigMinus
bigMinus는 큰 수에서 작은 수를 빼기 때문에 덧셈과 구현내용이 거의 비슷하다.
temp에 A를 더하고 B는 뺀다.
BIG_NUMBER bigMinus(BIG_NUMBER& a, BIG_NUMBER& b)
{
BIG_NUMBER result = { 0 };
int lenA, lenB, len;
char temp[MAX] = { 0 };
lenA = a.len;
lenB = b.len;
len = (lenA > lenB) ? lenA : lenB;
for (int i = 0; i < lenA; i++) temp[i] += a.num[i];
for (int i = 0; i < lenB; i++) temp[i] -= b.num[i];
for (int i = 0; i < len; i++)
{
if (temp[i] < 0)
{
temp[i] += 10;
temp[i + 1]--;
}
}
while (len && temp[len] == 0) len--;
len++;
for (int i = 0; i < len; i++) result.num[i] = temp[i];
result.len = len;
return result;
}
bigMultiply
A에서 i 번째 수와 B에서 k 번째 수를 곱하면 i + k 번째 자릿수에 덧셈을 하는 것이 곱셈이다.
이중 for문을 돌면서 곱셈을 끝내고 덧셈처럼 자릿수 올림을 처리하면 된다.
BIG_NUMBER bigMultiply(BIG_NUMBER& a, BIG_NUMBER& b)
{
BIG_NUMBER result = { 0 };
int lenA, lenB, len;
int temp[MAX] = { 0 };
lenA = a.len;
lenB = b.len;
for (int i = 0; i < lenA; i++)
for (int k = 0; k < lenB; k++)
temp[i + k] += (a.num[i] * b.num[k]);
len = lenA + lenB + 1;
for (int i = 0; i < len; i++)
{
temp[i + 1] += (temp[i] / 10);
temp[i] %= 10;
}
while (len && temp[len] == 0) len--;
len++;
for (int i = 0; i < len; i++) result.num[i] = temp[i];
result.len = len;
return result;
}
문자열 변환과 출력
문자열을 BIG_NUMBER로 변환하는 함수와 BIG_NUMBER를 문자열로 변환하는 함수,
그리고 음의 부호가 있는 경우 출력하기 위한 showResult 함수는 다음과 같다.
-0이 출력되지 않도록 len = 1이고 num[0] == 0인 경우는 부호를 +로 바꾼다.
void makeNumber(BIG_NUMBER& big, char number[MAX])
{
int len;
for (len = 0; number[len]; len++);
for (int i = 0; i < len; i++) big.num[len - 1 - i] = number[i] - '0';
big.len = len;
}
void changeIntToChar(BIG_NUMBER& big, char answer[MAX])
{
int len = big.len;
for (int i = 0; i < len; i++) answer[len - 1 - i] = big.num[i] + '0';
answer[len] = 0;
}
void showResult(BIG_NUMBER& result)
{
char answer[MAX] = { 0 };
if (result.len == 1 && result.num[0] == 0) result.sign = 1;
changeIntToChar(result, answer);
if (result.sign == -1) printf("-");
printf("%s\n", answer);
}
연산
이제 위의 함수를 이용해서 긴 자리 계산을 해보자.
A와 B의 부호가 +, + / +, - / -, + / -, - 인 경우를 나눠서 덧셈, 뺄셈, 곱셈을 실행하면 된다.
+, +
이 경우 덧셈 연산은 반드시 부호가 + 가 된다.
뺄셈인 경우 A > B라면 부호가 +, A < B라면 - 가 된다.
이렇게 경우를 나누면 덧셈과 뺄셈은 아래와 같이 경우가 나뉜다.
if (A.sign == 1 && B.sign == 1)
{
result = bigPlus(A, B);
result.sign = 1;
showResult(result);
if (isBig(A, B))
{
result = bigMinus(A, B);
result.sign = 1;
}
else
{
result = bigMinus(B, A);
result.sign = -1;
}
showResult(result);
}
+, -
이 경우 덧셈은 A가 더 큰 경우 +, B가 더 큰 경우 -다.
그리고 실제로는 빼는 연산이므로 bigMinus를 이용하고 부호만 변경한다.
반대로 A와 B의 크기에 상관없이 뺄셈 연산은 사실 덧셈 연산이므로, 더하면 된다.
else if (A.sign == 1 && B.sign == -1)
{
result.sign = 1;
if (isBig(A, B))
{
result = bigMinus(A, B);
result.sign = 1;
}
else
{
result = bigMinus(B, A);
result.sign = -1;
}
showResult(result);
result = bigPlus(A, B);
result.sign = 1;
showResult(result);
}
-, + / -, - 는 전체 코드를 참고하자.
곱셈은 부호가 다른 경우 -, 같으면 +로 처리하면 된다.
전체 코드는 다음과 같다.
#include <stdio.h>
#define MAX (2010)
typedef struct st
{
char num[MAX];
int len;
int sign;
}BIG_NUMBER;
bool isBig(BIG_NUMBER& a, BIG_NUMBER& b)
{
if (a.len != b.len) return a.len > b.len;
for (int i = a.len - 1; i >= 0; i--)
{
if (a.num[i] != b.num[i]) return a.num[i] > b.num[i];
}
return true;
}
BIG_NUMBER bigPlus(BIG_NUMBER& a, BIG_NUMBER& b)
{
BIG_NUMBER result = { 0 };
int lenA, lenB, len;
int temp[MAX] = { 0 };
lenA = a.len;
lenB = b.len;
len = (lenA > lenB) ? lenA : lenB;
for (int i = 0; i < lenA; i++) temp[i] += a.num[i];
for (int i = 0; i < lenB; i++) temp[i] += b.num[i];
for (int i = 0; i < len; i++)
{
temp[i + 1] += (temp[i] / 10);
temp[i] %= 10;
}
len++; // 자릿수가 오른 경우 체크
while (len && temp[len] == 0) len--;
len++;
for (int i = 0; i < len; i++) result.num[i] = temp[i];
result.len = len;
return result;
}
BIG_NUMBER bigMinus(BIG_NUMBER& a, BIG_NUMBER& b)
{
BIG_NUMBER result = { 0 };
int lenA, lenB, len;
char temp[MAX] = { 0 };
lenA = a.len;
lenB = b.len;
len = (lenA > lenB) ? lenA : lenB;
for (int i = 0; i < lenA; i++) temp[i] += a.num[i];
for (int i = 0; i < lenB; i++) temp[i] -= b.num[i];
for (int i = 0; i < len; i++)
{
if (temp[i] < 0)
{
temp[i] += 10;
temp[i + 1]--;
}
}
while (len && temp[len] == 0) len--;
len++;
for (int i = 0; i < len; i++) result.num[i] = temp[i];
result.len = len;
return result;
}
BIG_NUMBER bigMultiply(BIG_NUMBER& a, BIG_NUMBER& b)
{
BIG_NUMBER result = { 0 };
int lenA, lenB, len;
int temp[MAX] = { 0 };
lenA = a.len;
lenB = b.len;
for (int i = 0; i < lenA; i++)
for (int k = 0; k < lenB; k++)
temp[i + k] += (a.num[i] * b.num[k]);
len = lenA + lenB + 1;
for (int i = 0; i < len; i++)
{
temp[i + 1] += (temp[i] / 10);
temp[i] %= 10;
}
while (len && temp[len] == 0) len--;
len++;
for (int i = 0; i < len; i++) result.num[i] = temp[i];
result.len = len;
return result;
}
void makeNumber(BIG_NUMBER& big, char number[MAX])
{
int len;
for (len = 0; number[len]; len++);
for (int i = 0; i < len; i++) big.num[len - 1 - i] = number[i] - '0';
big.len = len;
}
void changeIntToChar(BIG_NUMBER& big, char answer[MAX])
{
int len = big.len;
for (int i = 0; i < len; i++) answer[len - 1 - i] = big.num[i] + '0';
answer[len] = 0;
}
void showResult(BIG_NUMBER& result)
{
char answer[MAX] = { 0 };
if (result.len == 1 && result.num[0] == 0) result.sign = 1;
changeIntToChar(result, answer);
if (result.sign == -1) printf("-");
printf("%s\n", answer);
}
int main()
{
int len;
char inputA[MAX], inputB[MAX];
BIG_NUMBER A, B, result = { 0 };
scanf("%s %s", inputA, inputB);
if (inputA[0] == '-')
{
makeNumber(A, inputA + 1);
A.sign = -1;
}
else
{
makeNumber(A, inputA);
A.sign = 1;
}
if (inputB[0] == '-')
{
makeNumber(B, inputB + 1);
B.sign = -1;
}
else
{
makeNumber(B, inputB);
B.sign = 1;
}
/* ------------------------------------------------------------------------- */
if (A.sign == 1 && B.sign == 1)
{
result = bigPlus(A, B);
result.sign = 1;
showResult(result);
if (isBig(A, B))
{
result = bigMinus(A, B);
result.sign = 1;
}
else
{
result = bigMinus(B, A);
result.sign = -1;
}
showResult(result);
}
else if (A.sign == 1 && B.sign == -1)
{
result.sign = 1;
if (isBig(A, B))
{
result = bigMinus(A, B);
result.sign = 1;
}
else
{
result = bigMinus(B, A);
result.sign = -1;
}
showResult(result);
result = bigPlus(A, B);
result.sign = 1;
showResult(result);
}
else if (A.sign == -1 && B.sign == 1)
{
if (isBig(B, A))
{
result = bigMinus(B, A);
result.sign = 1;
}
else
{
result = bigMinus(A, B);
result.sign = -1;
}
showResult(result);
result = bigPlus(A, B);
result.sign = -1;
showResult(result);
}
else // if (A.sign == -1 && B.sign == -1)
{
result = bigPlus(A, B);
result.sign = -1;
showResult(result);
if (isBig(A, B))
{
result = bigMinus(A, B);
result.sign = -1;
}
else
{
result = bigMinus(B, A);
result.sign = 1;
}
showResult(result);
}
result = bigMultiply(A, B);
result.sign = A.sign * B.sign;
showResult(result);
return 0;
}
'알고리즘 > BAEKJOON' 카테고리의 다른 글
BOJ 12015 : 가장 긴 증가하는 부분 수열 2 (1) | 2024.12.20 |
---|---|
BOJ 10757 : 큰 수 A+B (0) | 2023.07.29 |
BOJ 7453 : 합이 0인 네 정수 (0) | 2023.04.07 |
BOJ 1991 : 트리 순회 (0) | 2023.04.07 |
BOJ 6593 : 상범 빌딩 (0) | 2023.03.26 |
댓글