행렬은 알고리즘 문제를 풀 때도 그렇고, 3D 코딩을 할때도 필요하고 여러모로 쓸 경우가 많은것 같아 여기 정리합니다.


먼저 나이브한 행렬곱셈을 구현해보면 다음과 같습니다. (관련 문제: 백준 2740번 행렬곱셈)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
ll A[101][101], B[101][101], C[101][101];
 
int main(void)
{
    int n, m; scanf("%d %d"&n, &m);
    for (int y = 0; y < n; y++)
    for (int x = 0; x < m; x++
        scanf("%lld"&A[y][x]);
 
    int k; scanf("%d %d"&m, &k);
    for (int y = 0; y < m; y++)
    for (int x = 0; x < k; x++
        scanf("%lld"&B[y][x]);
 
    for (int y = 0; y < n; y++) {
        for (int x = 0; x < k; x++) {
            for (int i = 0; i < m; i++
                C[y][x] += A[y][i] * B[i][x];
            
            printf("%lld ", C[y][x]);
        }
        printf("\n");
    }
 
    return 0;
}
cs


특별할건 없고, 2차원 배열로 잡고 y, x (또는 행, 열 이라고도 표현 가능) 순으로 인덱스 할당해서 계산한다는 것..


근데 한가지 불편한점이 발견되는것은 곱셈결과를 리턴하도록 함수로 뺄 경우에 행렬을 리턴값으로 두기 까다롭다는 것이다 (2차원 배열이니까.. 한가지 work around는 걍 out-value 파라미터를 사용하는것.. 하지만 그렇더라도 리턴값처럼 복사기반이 아니기 때문에 값의 정합성(오버라이팅 된다거나..)에 대해 항상 고민해야한다.) 

따라서 자연스럽게 2차원배열 그 자체로 쓰는것보다 struct나 class로 빼는걸 생각하게 되는데.. 대입연산자나 생성자, 소멸자, 멤버함수등 생각할게 많아지고 코드의 단순함이 많이 훼손(?)된다.


vector의 vector를 사용하면 위의 리턴문제를 비교적 간단하게 해결할 수 있다. 아래 코드를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <bits/stdc++.h>
using namespace std;
 
typedef vector<vector<int>> matrix;
 
matrix mul(matrix A, matrix B)
{
    int n = A.size(), m = A[0].size(), k = B[0].size();
    matrix C(n, vector<int>(k));
 
    for (int y = 0; y < n; y++)
    for (int x = 0; x < k; x++)
    for (int i = 0; i < m; i++)
        C[y][x] += A[y][i] * B[i][x];
    return C;
}
 
int main(void)
{
    int n, m; scanf("%d %d"&n, &m);
    matrix A(n, vector<int>(m));
    for (int y = 0; y < n; y++)
    for (int x = 0; x < m; x++
        scanf("%d"&A[y][x]);
 
    int k; scanf("%d %d"&m, &k);
    matrix B(m, vector<int>(k));
    for (int y = 0; y < m; y++)
    for (int x = 0; x < k; x++
        scanf("%d"&B[y][x]);
 
    matrix C(n, vector<int>(k));
    C = mul(A, B);
    for (int y = 0; y < n; y++) {
        for (int x = 0; x < k; x++) {
            printf("%d ", C[y][x]);
        }
        printf("\n");
    }
 
    return 0;
}
cs



연산자 오버로딩을 사용하면 mul 함수대신 * 기호를 사용할수도 있다. 다음을 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <bits/stdc++.h>
using namespace std;
 
typedef vector<vector<int>> matrix;
 
matrix operator * (matrix A, matrix B) 
{
    int n = A.size(), m = A[0].size(), k = B[0].size();
    matrix C(n, vector<int>(k));
 
    for (int y = 0; y < n; y++)
    for (int x = 0; x < k; x++)
    for (int i = 0; i < m; i++)
        C[y][x] += A[y][i] * B[i][x];
    return C;
}
 
int main(void)
{
    int n, m; scanf("%d %d"&n, &m);
    matrix A(n, vector<int>(m));
    for (int y = 0; y < n; y++)
    for (int x = 0; x < m; x++
        scanf("%d"&A[y][x]);
 
    int k; scanf("%d %d"&m, &k);
    matrix B(m, vector<int>(k));
    for (int y = 0; y < m; y++)
    for (int x = 0; x < k; x++
        scanf("%d"&B[y][x]);
 
    matrix C(n, vector<int>(k));
    C = A * B;
    for (int y = 0; y < n; y++) {
        for (int x = 0; x < k; x++) {
            printf("%d ", C[y][x]);
        }
        printf("\n");
    }
 
    return 0;
}
cs


반응형

'Programming > Problem Solving' 카테고리의 다른 글

C++ bigint class  (0) 2020.03.30
백준 9663번 N-Queen  (0) 2020.03.15
C언어에서 표준입력으로 string 입력 받기  (0) 2020.02.21
백준 팁모음  (0) 2019.03.18
백준 제출시 오류유형 정리  (0) 2019.03.18

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

이 문제 처럼 여러줄의 문장을 입력으로 받는 경우를 생각해보자.


나이브하게 생각하면 아래처럼 하면 될 것 같지만..

1
2
3
4
5
    char line[100];
    while(1){
        scanf("%s", line);
        if(strcmp(line, ".")==0break;
    }
cs


막상해보면 공백이나 줄바꿈 단위로 끊어져서 단어 단위로 들어옴을 알 수 있다. (더군다나 공백이나 줄바꿈 정보는 소실된다)

만약 원하는게 단어단위로 끊어서 처리하는거라면 이렇게 처리해도 상관은 없다. 


하지만 이 문제의 경우처럼 줄바꿈 정보가 필요한 경우는 다른 방법이 필요하다.


1
2
3
4
5
6
    char line[100];
    while(1){
        gets(line);
        if(strcmp(line, ".")==0break;
    }
 
cs

그런경우 이런식으로 scanf 대신 gets를 쓰면 줄단위로 받는게 가능하다.

근데 황당하게도 C++14부터는 gets를 지원하지 않는다. ㄷ ㄷ ㄷ

따라서 이함수를 쓰려면 컴파일러를 C로 두거나 C++11이하로 두어야 한다.

또한가지 번거로움이 있는데 visual studio 2017에서는 gets를 지원하지 않아서 gets_s로 바꿔야 한다 ㅠ(근데 또 이대로 제출하면 안됨)


fgets(line, sizeof(line), stdin);  이거는 visual studio, gcc 둘다되는것 같다.


scanf("%[^\n]", str); 신기하게도 이것도 된다.
scanf("%99[^\n]", str); 좀더 safe하게 하려면 이렇게 100-1 숫자를 앞에 적어주면 된다. 1은 \n용


반응형

'Programming > Problem Solving' 카테고리의 다른 글

C++ bigint class  (0) 2020.03.30
백준 9663번 N-Queen  (0) 2020.03.15
행렬코딩  (0) 2020.03.01
백준 팁모음  (0) 2019.03.18
백준 제출시 오류유형 정리  (0) 2019.03.18

 

 

c++에서 string 입력받기

이 문제에서 처럼, 공백이 포함된 한 줄 전체를 string에 받아올 일이 있을때는 아래처럼 getline을 쓰면 된다.

1
2
string str;
getline(cin, str);
cs

 

c++로 입출력하기

앞에거는 printf,scanf랑 페어링 끄기, 뒤에거는 입출력반복될때 처리에 대한것

1
ios::sync_with_stdio(0);cin.tie(0);
cs

이거 하고 나면 printf, scanf쓰면 안된다.

헤더파일 단순화 하기

알고리즘 문제를 풀다보면 다음과 같이 헤더파일이 점점 길어지고 지저분해짐을 알 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
#include <set>
#include <stack>
#include <queue>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <map>
 
using namespace std;
 
int dp[2][1000000+1];
int main(void)
{
cs

 

그 이유는 매번 문제마다 필요한 vector, map, string등을 그때 그때 추가하다보면 귀찮아서져 그냥 슈퍼셋으로 들고가게 되는 현상이 나타난다(...)

 

근데 gcc에서만 지원하긴하지만 다음처럼 <bits/stdc++h>를 쓰면 헤더파일이 무척 깔끔해진다. 자주쓰는 헤더들을 모아서 처리해주는 역할을 해주기 때문이다.

 

1
2
3
4
5
6
7
#include <bits/stdc++.h>
using namespace std;
 
int main(void)
{
    return 0;
}
cs

 

표준은 아니라서 visual c++나 xcode에서는 잘 안되는데,

xcode 기반의 clion을 쓰는 내 경우는 여기 링크에 나온대로 stdc++.h파일을 복사한다음에 다음경로에 설정해주니 잘 되었다.

만약에 bits/stdc++.h의 경로를 못찾겠다고 나오면,

CMakeLists.txt를 열어서 다음 부분을 추가해주면 되었다(include_directories 부분)

 

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1

mac에서 그냥 c++을 쓰는경우 (visual studio code등에서 사용) 다음 경로에 설정해주면 된다.

 

/usr/local/include

 

visual studio를 쓰는 경우는 아래 링크에 복사

 

D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.31.31103\include

 

그래프 그려주는 툴

https://csacademy.com/app/graph_editor/ 정말 짱인거 같다.

 

stl을 printf-style로 디버깅하기

visual studio가 라인디버깅 성능이 막강하긴 하지만, PS를 하다가 그렇게 하다보면 시간이 너무 많이 흘러가게 된다.

따라서 간단하게 vector나 map등을 print로 찍어보면 좋은데, built-in으로는 그러한 함수가 없고 반드시 루프를 돌려야 한다.

여기 있는 헤더를 include하면 그러한 점을 보완해준다.

 

간단하게 사용법을 보려면 다음 예제를 보자.

1
2
3
4
5
6
7
8
9
#include <bits/stdc++.h>
#include "dbg.h"  // https://github.com/sharkdp/dbg-macro
using namespace std;
int main()
{
    map<intstring> m = { {1,"abc"}, {2,"cde"} };
    dbg(m);
    return 0;
}
cs

 

dbg()로 감싸주기만 하면 그내용을 다음처럼 출력해준다.

1
[..\nothing\nothing.cpp:7 (main)] m = {{1"abc"}, {2"cde"}}
cs

 

물론 온라인저지에 이대로 제출할수는 없기 때문에(커스텀 헤더라, 저지 서버에서는 인식못하고, 속도도 떨어뜨릴거고)

다음과 같이 감싸주는 부분이 필요하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <bits/stdc++.h>
using namespace std;
#ifdef WIN32
#include "dbg.h"  // https://github.com/sharkdp/dbg-macro
#else
#define dbg(...)
#endif
int main()
{
    map<intstring> m = { {1,"abc"}, {2,"cde"} };
    dbg(m);
    return 0;
}
cs

 

나는 간단하게 WIN32으로 감싸주었지만, 좀 더 고민해서 잘 감싸는게 필요할지도 (-DONLINE_JUDGE등 사용)

 

 

입력개수가 명시되지 않은 입력을 받는법

예를 들어 이 문제의 경우 입력개수 없이 받도록 되어 있다. 

while(cin << n) 이렇게 받는걸로 추천되어 있다.

c스타일로 받을경우는 다음처럼 하면 된다.

1
2
3
4
int n;
while(scanf("%d"&n)!=EOF){
    //do something
}
cs

이 문제에서 연습할 수 있다.

 

freopen 사용하기

백준에서는 ONLINE_JUDGE를 define하고 있으므로 다음과 같이 사용하면 파일로 입력을 줄 수 있다.

1
2
3
#ifndef ONLINE_JUDGE
    freopen("input.txt""rt", stdin);
#endif
cs

 

char를 입력받는 방법

이 문제를 보면 숫자를 입력받은 다음에 char를 입력받게 되어 있는데 생각없이 scanf("%d") 한다음에 scanf("%c")를 하게 되면 \n char 때문에 제대로 안된다. 방법은 scanf("%c") 대신에 scanf("\n%c")로 해주면 되긴했다.

또는 숫자는 scanf("%d") 로 받고 그다음은 scanf("%s")로 문자열로 받아서 문자열 파싱을 해도 됨

 

EOF까지 입력받기

이 문제와 같이 N이주어지는게 아니라 단순히 EOF까지 입력을 받으라는 경우가 있다.

이때는 C언어의 경우는 다음처럼 scanf의 리턴값이 EOF임을 체크하면 된다.

#include <cstdio>

int main() {
    int n;
    while (scanf("%d", &n) != EOF) {
        // process the input
    }
    return 0;
}

 

개행문자까지 문자열을 읽는 방법

3 @ %
10.4 # % @
8 #

위와 같이 어쩔때는 문자가 2개 (@ X), 어쩔때는 문자가 3개 (# % @), 어쩔때는 문자가 한개(#) 주어질때,

아래처럼 %[^\n]를 쓰면 개행문자까지 문자열을 읽을 수가 있다.

double A;
char s[10] = {};
scanf("%lf %[^\n]", &A, s);
for (int i = 0; s[i] != '\0'; ++i) {
    if (s[i] == '@') A *= 3;
    else if (s[i] == '%') A += 5;
    else if (s[i] == '#') A -= 7;
}

이 문제를 참조하자.

반응형

'Programming > Problem Solving' 카테고리의 다른 글

C++ bigint class  (0) 2020.03.30
백준 9663번 N-Queen  (0) 2020.03.15
행렬코딩  (0) 2020.03.01
C언어에서 표준입력으로 string 입력 받기  (0) 2020.02.21
백준 제출시 오류유형 정리  (0) 2019.03.18


런타임오류

1. 배열 크기가 너무 작게 잡혔거나


2. 다음처럼 T로 여러 테스트 케이스를 돌리는데 배열이나 변수가 그 밖에 선언되어 있는 경우 발생가능

1
2
3
4
5
6
7
8
 
int t[1001],pre[1001],r[1001];
vector<int> v[1001];
int main(void)
{
    int T;scanf("%d",&T);
    while(T--){
        
cs


반응형

'Programming > Problem Solving' 카테고리의 다른 글

C++ bigint class  (0) 2020.03.30
백준 9663번 N-Queen  (0) 2020.03.15
행렬코딩  (0) 2020.03.01
C언어에서 표준입력으로 string 입력 받기  (0) 2020.02.21
백준 팁모음  (0) 2019.03.18

+ Recent posts