[문제출처 : Codechef] $N\times N$ 행렬이 있습니다. 초기에 이 행렬은 0들로 채워져있습니다. 이 때 두 가지 명령으로 연산을 한다고 가정해봅시다.
RowAdd R X : R번째 행의 모든 원소를 X만큼 증가시킵니다. ColAdd C X : C번째 열의 모든 원소를 X만큼 증가시킵니다.
이 때 주어진 명령어들을 수행하고 나온 결과 중 grid내에 가장 값이 큰 원소가 무엇인지 리턴하는 것이 문제입니다.
여기서 가장 무식하게 푸는 방법은 정말로 $N\times N$ 행렬을 만들어서 거기에 명령어대로 곧이 곧대로 다 더해보고 maximum값을 찾는 것입니다. 이것을 하기 위해서는 일단 메모리가 $N^2$만큼 필요하구요, 각 연산마다 $N$번의 덧셈을 해야하고, 마지막으로 최대값을 찾는데에 또 $N^2$의 연산이 필요하게 됩니다. 결국 \[\begin{aligned} \text{Memory Usage}&=N^2 \newline \text{Complexity}&=O(kN)+O(N^2) \end{aligned} \] 으악.. 뭔가 보기 싫은 숫자들이네요 ^^; 그러면 어떻게 하면 조금 더 효율적인 코딩을 할 수 있을까요? 다음과 같은 방법을 생각해봅시다.
크기 N의 row vector와 col vector를 각각 정의합니다.
RowAdd할 경우 해당 r번째 row vector의 원소만을 더하고, col vector에 아무 연산도 하지 않습니다. 반대로 ColAdd할 경우 해당 c번째 col vector의 원소만을 더합니다.
각 row vector와 col vector의 max값은 더하면서 계속 tracking합니다.
답은 max_in_rowVec + max_in_colVec이 됩니다.
이렇게 하게 될 경우 \[\begin{aligned} \text{Memory Usage}&=2N\newline \text{Complexity}&=O(k) \end{aligned} \]
어마어마한 발전이죠? 이 문제에서는 오랜만에 C언어를 사용했는데요, string
을 scanf
하는 방법과 calloc
(메모리 공간을 할당하면서 0으로 초기화하는 함수)를 연습할 수 있는 좋은 기회가 될 것 같습니다.
#include <stdio.h>
#include <stdlib.h>
int main(){
/* 행렬의 크기 N과 명령어의 개수 Q를 받아옵니다.*/
int N, Q;
scanf("%d %d", &N, &Q);
//명령어가 6글자면 반드시 +1만큼 buffer를 만들어서 string으로 받아올 수 있게 해줘야 합니다.
char command[7];
//연산할 line index와 얼마를 더할지를 저장하는 변수
int line, added;
/* calloc을 이용한 int array만드는 것입니다. */
int *rows = (int*)calloc(N, sizeof(int));
int *cols = (int*)calloc(N, sizeof(int));
//각 row, col vector에서의 최대값을 저장할 변수입니다.
int maxr=0, maxc=0;
int i, j;
//명령어 개수만큼 돌면서
for(i=0;i<Q;i++){
//명령어 정보들을 읽어옵니다.
scanf("%s %d %d", command, &line, &added);
if(command[0]=='R'){ //명령어가 row 연산이면,
rows[line-1] += added; //더하고,
if(rows[line-1]>maxr)
maxr=rows[line-1]; //max값을 track합니다.
}
//col 연산일 경우도 마찬가지입니다.
else{
cols[line-1] += added;
if(cols[line-1]>maxc)
maxc = cols[line-1];
}
}
//결국 최종 정답은 maxr+maxc가 됩니다.
printf("%d\n", maxr+maxc);
//항상 메모리를 free해주는 것을 잊으시면 안되겠죠? :)
free(rows);
free(cols);
return 0;
}
Input:
2 4
RowAdd 1 3
ColAdd 2 1
ColAdd 1 4
RowAdd 2 1
Output:
7
결과가 잘 나옴을 확인할 수 있네요. 정말 무턱대고 구현하는 것과, 효율적인 알고리즘을 고민한 후에 짜는 것이 얼마나 큰 차이를 불러오는지 다시한번 확인할 수 있는 좋은 기회였습니다 :)