본문 바로가기
알고리즘

[백준] 행렬 덧셈 [코드 + 패턴을 찾아보고 풀어보는건 어떨까?]

by itstime0809 2023. 8. 31.
728x90

구현

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

 

2738번: 행렬 덧셈

첫째 줄에 행렬의 크기 N 과 M이 주어진다. 둘째 줄부터 N개의 줄에 행렬 A의 원소 M개가 차례대로 주어진다. 이어서 N개의 줄에 행렬 B의 원소 M개가 차례대로 주어진다. N과 M은 100보다 작거나 같

www.acmicpc.net

 

코드

#include <iostream>
using namespace std;

int main(void) {
	ios_base::sync_with_stdio(false); cin.tie(NULL);


	int n, m;
	cin >> n >> m;	

	int** array = new int*[2*n];

	for (int i = 0 ; i < 2*n; i++){
    
		array[i] = new int[m];
        
		fill_n(array[i], m, 0);
		for (int j = 0; j < m; j++) {
			cin >> array[i][j];
		}
	}	


	for (int i = 0; i < 2*n/2; i++) {
		for (int j = 0; j < m; j++) {
			cout << array[i][j] + array[2*n/2+i][j] << " ";
		}
		cout << "\n";
	}
	
	return 0;
}

 

 설명

 이번 문제는 C++을 사용해서 2차원 배열을 활용하는 문제다. C++을 연습하기 시작하면서 해당 문제를 풀어 보았는데 브론즈 단계에 문제라 어려운 것은 없지만 행렬의 덧셈을 조금 더 패턴을 찾아가며 풀어보고 싶었다. 별 찍기와 유사하게 요즘 패턴을 분석해서 코드에 옮기려는 시도를 자주 한다. 식을 도출해 내면 그건 그것대로 기분도 좋지만 여러 방면으로 생각해 볼 수 있었다는 사실에 만족해한다. 다시 본론으로 돌아와 해당 문제는 다시 말하지만 어려운 문제는 아니다. 행렬의 덧셈에 대해서 어떻게 덧셈이 이루어지는지 알고 있다면 그것대로 코드를 구현하면 되는 문제다. 기본 적인 방법으로 풀어본다면 A행렬과 B행렬을 각 배열을 만들어주고 A행렬과 B행렬의 입력을 따로 받아주는 것이다. 그리고 마지막으로 for문을 돌면서 각 원소끼리 더해주면 그게 답이 된다. 그럼 이 코드는 다른 점이 무엇일까? 동적 할당이라는 방법을 알게 되었다. 동적 할당이라고 하면 N만큼의 길이가 주어지면 그 안에 들어갈 원소를 원하는 M만큼의 크기로 채울 수 있다. 이후 M만큼으로 설정된 원소들만큼 입력을 받아주면 된다. C++ STL을 사용하는 vector방법도 있지만 C++을 처음 연습해 보는 것이기 때문에 STL에 의존하지 않고 기본적인 배열의 형태로만 가지고 연습하고 있기 때문에 더 좋은 풀이 방법이 존재한다. 따라서 해당 포스트에서는 이렇게 패턴을 찾을 수도 있구나를 중점적으로 바라보면 좋을 것 같다.

 

 길게 이야기 할 것 없이 입력을 받는 부분에서 2*n번만큼 입력을 받고 있다. 왜냐하면 행렬의 덧셈은 기본적으로 같은 꼴일 때만 성립하기 때문에 N과 M을 입력받았다면 두 행렬은 N과 M으로 동일한 크기를 지녀야 한다는 뜻이 된다. 그래야 행렬의 덧셈이 되기 때문이다. 행렬의 덧셈이 되지 않는다는 조건은 명시해 둔 것이 없기 때문에 행렬의 덧셈이 가능하다는 가정을 가지고 간다. 물론 행렬의 덧셈 조건이 되지 않는 조건이 주어진다면 해당 코드는 틀린 코드가 되겠지만 이 문제 같은 경우는 그러한 경우가 존재하지 않으니 신경 쓰지 않는다. 다시 돌아와서 2*n만큼 받는다는 것은 다른 말로 말하면 같은 크기의 N행만큼을 한 번 더 받는다는 의미가 된다. M은 그렇게 받을 필요가 없는 게 각 원소가 주어지는 길이는 모두 M으로 동일하기 때문이다. 따라서 A와 B의 배열을 한 번에 받는다고 보면 된다. 어차피 i 가 증가할 때마다 2*n개의 행을 생성해 놨기 때문에 자동적으로 A를 받게 되고 B를 이어서 받게 된다. 그렇기 때문에 입력을 받는 for문을 두 번 구성할 필요가 없다. 

 

 그렇게 입력을 모두 받게 되면 이제 계산해줄 일만 남았다. 아주 사소한 부분에서부터 패턴을 찾기 시작하는 연습이 중요하다고 생각한다. 이 문제 같은 경우도 그러한 부분에서 시작한다. 먼저 아래에 주어진 정보들을 활용할 수 있는지부터 확인한다.

 

 

N, M 은 모두 3, 3이라고 가정한다.

i =0, j = 3

i=1, j = 4

i= 2, j = 5

 

 

 위 조건들은 A와 B의 행렬 덧셈을 하기 위해서 A의 첫 번째 행과 B의 첫 번째 행의 위치를 i, j로 표현한 것이다. 따라서 i = 0이라는 것은 A의 첫 번째 행을 의미하며 첫 번째 행은 누구와 덧셈을 진행해야 되는지를 살펴보면 2*N만큼 입력을 받았기 때문에 A의 부분은 2*N / 2까지로 볼 수 있다. 그렇다면 B는 2*N / 2 + 1 부분부터 B가 시작되기 때문에 j = 3이 된다는 것을 찾아낼 수 있다. 다음 부분도 마찬가지로 i = 1일 때면 2*N / 2 + 2 부분이 그다음 덧셈의 대상이 될 것이다. 이렇게 i .. n까지 갈 때 j는 2*N / 2 + i 만큼 위치에 있는 행과 덧셈을 진행할 수 있다는 사실을 알 수 있다. 따라서 해당 수식을 그대로 B행렬에 적용하면 기본적인 코드에 비해서 입력을 받는 부분을 최소화할 수 있으며 동시에 패턴화 해볼 수 있는 기회가 생기게 된다고 생각한다. 항상 패턴화를 연습하려고 하는데 좀처럼 잘 되지 않을 때면 위와 같이 내가 알고 있는 정보들을 가지고 추론해 나가는 방법도 한 가지 방법인 것 같다.