[개념]

간단하게 설명하자면 하드링크는 동일한 파일에 다른 이름을 붙히는 것입니다. 심볼릭 링크는 바로가기 입니다.

원본 파일의 하드링크를 만들었을 때 이 둘은 동일한 파일이기 때문에 하나를 변경하는 것은 둘 모두를 변경하는 것과 같습니다. C++의 참조자라고 생각하면 좋겠네요. 하드링크는 동일한 변수를 서로다른 이름으로 부르는 C++의 참조자와 비슷합니다. 대용이라는 친구가 있고 별명이 빅드래곤이라고 가정한다면 대용이라고 부르던 빅드래곤이라고 부르던 동일한 사람을 부르는 것이죠. 마찬가지로 /home/sinsisao/test.original이라는 파일과 그 하드링크인 /home/sinsisao/UbuntuStudy/test.hardlink가 있다면 경로나 파일 이름은 다르지만 둘은 동일한 데이터 블록을 가진 동일한 파일을 칭하는 것입니다. 한 가지 유의할 점은 둘중 하나를 삭제한다는 것의 의미가 파일의 삭제가 아닌 하드링크의 삭제라는 것입니다. 사용하던 이름 하나를 없애는거라고 생각하시면 쉽게 이해가 될듯합니다. 어떤 경우에 하드링크를 주로 사용하는지는 잘 모르겠습니다. 예상해보건데 아마 백업용으로 사용하지 않을까 싶네요.

+ 디렉터리의(폴더의) 하드링크를 만들 수 없습니다.

+ 다른 디스크에 하드링크를 만들 수 없습니다. 예를 들어 C드라이브에 있는 파일의 하드링크를 D드라이브에 만들 수 없습니다. 이유는 잘 모르겠습니다.

 

심볼릭 링크는 "윈도우 바로가기 만들기"의 바로가기와 비슷합니다. 말그대로 특정 경로의 파일을 가리킵니다. 해당 파일이 삭제되거나 다른 경로로 이동된다면 바로가기를 사용할 수 없습니다. 사용예로는 하드디스크를 /mnt/hdd와 같은 곳에 마운트한 후 본인의 home에 심볼릭 링크를 만들어 이용할 수 있겠습니다.

또, 잘 알진 못하지만 alternatives 라는 것에서 사용하는듯 합니다. alternatives의 대략적인 개념은 test2.2test3.3 식으로 test라는 프로그램의 2.2버전과 3.3버전 바이너리를 모두 가지고 있을 때, 사용하고자하는 버전 바이너리의 심볼릭 링크를 test 라는 이름으로 만들어서 쓰는 겁니다. 아마 맞을걸요...

+ 디렉터리의 심볼릭링크를 만들 수 있습니다.

+ 다른 디스크에 심볼릭링크를 만들 수 있습니다.

 

[사용]

// hard link
ln xxx yyy
// symbolic link
ln -s xxx yyy

하드링크, 심볼릭 링크 모두 ln 이라는 동일한 명령어를 사용해서 만듭니다. 다만, symbolic link 의 경우 -s 옵션을 줍니다. xxx 는 원본 파일을 의미하고 yyy는 새로 생성할 링크 파일을 의미합니다.

'개발 > linux' 카테고리의 다른 글

[기초편] Shell 이란?  (0) 2020.08.24
vi 사용법  (0) 2020.06.16
리눅스 명령어 약자 확인하기  (0) 2020.05.28
우분투 터미널 명령행에서 쓰던 단어, 행 지우는 단축키  (0) 2020.05.14
유용한 링크 모음  (0) 2020.05.10

man 이라는 명령어가 있습니다. manual 을 뜻하며 명령어 다음에 오는 인수에 대한 설명을 제공합니다. 이 man 명령어를 통해 특정 명령어가 어떤 이름의 축약인지 확인할 수 있습니다. 사용의 예는 아래와 같습니다.

:~$ man man
// an interface to the on-line reference manuals ...
:~$ man ls
// list directory contents ...
:~$ man pwd
// print name of current/working directory ...
:~$ man mv
// move (rename) files ...

물론 그렇다고 man 명령어로 모든 명령의 내용을 확인할 수 있는 것은 아닙니다. man 명령어로 확인이 가능하려면 보이고자 하는 메뉴얼을 프로그램 내부 어딘가에 man에서 규정하는 양식으로 작성해 놓아야 볼 수 있지 않을까? 생각합니다. 한 예로 change directory 를 뜻하는 cd 명령의 경우 man 명령으로 확인할 수 없습니다.

:~$ man cd
No manual entry for cd

'개발 > linux' 카테고리의 다른 글

vi 사용법  (0) 2020.06.16
하드링크, 심볼릭링크 개념 및 사용법  (0) 2020.06.02
우분투 터미널 명령행에서 쓰던 단어, 행 지우는 단축키  (0) 2020.05.14
유용한 링크 모음  (0) 2020.05.10
서문  (0) 2020.05.10
user@host:~$ man date|

위와 같이 명령으로 man date를 치고 엔터를 누르기 전 커서가 맨 뒤에 있는 상황에서

    * 바로 앞의 date 단어를 지우고 싶다면 Ctrl + w

    * 모든 입력을 지우고 싶다면 Ctrl + u

를 입력하면 됩니다. 사실 별거 아니고 되게 간단한 거지만 책을 읽기 전까진 모르고있었네요.

shift + home 이 안되서 많이 불편했지만 그냥저냥 잘살았었죠. 이젠 잘 지울 수 있습니다. ㅎㅎ

그냥 엔터를 연타하면 편하지만 위 단축키가 요긴하게 사용될만한 상황도 있겠죠? 어쨋든 아는 건 힘이니까요. :)

'개발 > linux' 카테고리의 다른 글

vi 사용법  (0) 2020.06.16
하드링크, 심볼릭링크 개념 및 사용법  (0) 2020.06.02
리눅스 명령어 약자 확인하기  (0) 2020.05.28
유용한 링크 모음  (0) 2020.05.10
서문  (0) 2020.05.10

* 만화로 보는 리눅스 역사 = "https://joone.net/"

* 커널 다운로드 = "https://www.kernel.org/"

* 리눅스 배포판 확인 = "http://futurist.se/gldt/"

 

'개발 > linux' 카테고리의 다른 글

vi 사용법  (0) 2020.06.16
하드링크, 심볼릭링크 개념 및 사용법  (0) 2020.06.02
리눅스 명령어 약자 확인하기  (0) 2020.05.28
우분투 터미널 명령행에서 쓰던 단어, 행 지우는 단축키  (0) 2020.05.14
서문  (0) 2020.05.10

https://www.hanbit.co.kr/store/books/look.php?p_code=B1192694090

 

IT CookBook, 우분투 리눅스(개정판)

시스템, 서버 및 네트워크, 보안까지 한 권으로 정복하는 우분투 리눅스

www.hanbit.co.kr

이전에 Chapter2의 Part7 까지 읽다가 접었던 책입니다. 항상 리눅스를 공부해야된다는 생각은 있었는데 생각만 있었죠...;; 실천이란 참 어려운 일입니다. 같은 이름의 책을 찾아보니 개정판이 나왔네요. 뭐 크게 차이가 있진 않을거라 생각합니다. 다시 한 번 정독하기 도전해봅니다!

제한은 1초 512MB

맵의 크기는 8*8, 카메라를 4방향으로 돌릴 수 있고 카메라의 최대 갯수는 8개입니다.

카메라 8대 각각의 방향을 한 번씩 바꿔볼 때 나올 수 있는 경우의 수는 4^8 = 2^16 = 64k 번입니다. 카메라 각각은 고유함으로 중복은 없습니다. 각 경우에 사각 지역을 찾는 것은 8*8로 대충 100입니다. 64k * 100 = 6.4m 번입니다. 대충 1g 번 명령을 수행할 때 1초가 걸린다고 계산하면 6.4m 번은 0.01초(10ms) 이내에 수행될 수 있다고 생각합니다. 물론 카메라의 감시 영역을 체크하면서 수행될 명령문은 생각하지 않았지만 이를 차치하고서라도 시간은 충분해보입니다.

중복 순열은 재귀문으로 간단하게 작성할 수 있습니다.

void 중복순열(int 카메라 번호) {	/* 최대 카메라 갯수 = 8 */
	if 모든 카메라의 방향을 조정 ?
        맵의 사각 지역을 카운트
        return;
    
    for (감시 방향)			/* 방향 = 4 */
        감시 체크
        중복순열(이번 카메라 번호 + 1) 	// 다음 카메라로 넘어감
        감시 해제
}        

 

다음은 감시 체크와 해제를 하는 부분입니다. 우선 한 지점으로부터 한 방향으로 인자에 따라 감시 체크나 해제를 하는 함수를 만들었습니다. 감시 체크시에는 맵에서 -1을 하고 해제시엔 +1을 했습니다. 음의 정수의 크기로 중복 감시되는 지역을 표현했습니다.

int check(int x, int y) {
    if (x < 0 || N <= x || y < 0 || M <= y || map[x][y] == 6) return -1;
    if (0 < map[x][y] && map[x][y] < 6) return 0;
    return 1;
}

void observe(int x, int y, int d, bool f) {		// x,y 지점으로부터 d 방향으로 f (감시or해제)합니다.
    int xx = x + dir[d][0];
    int yy = y + dir[d][1];
    int c = check(xx, yy);

    while (c >= 0) {
        if (c) {
            map[xx][yy] += f ? -1 : 1;
        }
        xx += dir[d][0];
        yy += dir[d][1];
        c = check(xx, yy);
    }
}

 

카메라 특성에 맞게 observe() 함수를 호출할 수 있도록 각 카메라에 대한 정보를 배열에 담아 참조하도록 했습니다.

int dir[4][2] = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} };

int spec[5/*cctv*/][5/*time*/][4/*direction*/] = {
    { {1, 4},  {0}, {1}, {2}, {3} },
    { {2, 2},  {0, 1}, {2, 3}, {}, {} },
    { {2, 4},  {0, 3}, {0, 2}, {1, 2}, {1, 3} },
    { {3, 4},  {0, 1, 2}, {0, 1, 3}, {0, 2, 3}, {1, 2, 3} },
    { {4, 1},  {0, 1, 2, 3}, {}, {}, {}}
};

3차원 배열이며 첫 번째는 카메라의 번호를 뜻합니다. 1번 카메라에 대한 정보는 spec[0] 번에 저장됩니다. 두 번째는 카메라 회전 횟수를 의미합니다. 동서남북 방향으로 4회가 최대입니다. 하지만, 특정 카메라의 경우엔 네 방향 모두 돌릴 필요가 없습니다. 예를 들어, 2번 카메라의 경우엔 가로, 세로 2번만 회전시키면 됩니다. 카메라마다 필요한 감시 횟수가 다름으로 이를 두 번째의 0번째 공간을 활용해 표현했습니다. 예를 들어 spec[0][0], {1, 4} 는 1번 카메라가 한 번에 1방향만 탐색하고 4회 회전해야함을 의미합니다. 마지막은 확인해야할 방향을 의미합니다. dir[][] 배열을 이용해서 0~3의 정수로 동서남북을 표현하였습니다.

전체 코드입니다.

#pragma warning(disable: 4996)
#include <stdio.h>
int N, M, map[10][10], ans = 0x7fffffff;
int cctv[10][2], K;
int dir[4][2] = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} };
int spec[5/*cctv*/][5/*time*/][4/*direction*/] = {
    { {1, 4},  {0}, {1}, {2}, {3} },
    { {2, 2},  {0, 1}, {2, 3}, {}, {} },
    { {2, 4},  {0, 3}, {0, 2}, {1, 2}, {1, 3} },
    { {3, 4},  {0, 1, 2}, {0, 1, 3}, {0, 2, 3}, {1, 2, 3} },
    { {4, 1},  {0, 1, 2, 3}, {}, {}, {}}
};

int check(int x, int y) {
    if (x < 0 || N <= x || y < 0 || M <= y || map[x][y] == 6) return -1;
    if (0 < map[x][y] && map[x][y] < 6) return 0;
    return 1;
}

void observe(int x, int y, int d, bool f) {
    int xx = x + dir[d][0];
    int yy = y + dir[d][1];
    int c = check(xx, yy);

    while (c >= 0) {
        if (c) {
            map[xx][yy] += f ? -1 : 1;
        }
        xx += dir[d][0];
        yy += dir[d][1];
        c = check(xx, yy);
    }
}

void solve(int cctvn) {
    if (cctvn >= K) {
        int area = 0;
        for (int i = 0; i < N; i++)
            for (int j = 0; j < M; j++)
                area += (map[i][j] == 0);
        if (area < ans)
            ans = area;
        return;
    }

    const int& x = cctv[cctvn][0];
    const int& y = cctv[cctvn][1];
    const int& cctvkind = map[x][y];
    const int& filltime = spec[cctvkind - 1][0][1];
    const int& fillcount = spec[cctvkind - 1][0][0];
    for (int i = 0; i < filltime; i++) {
        for (int j = 0; j < fillcount; j++)
            observe(x, y, spec[cctvkind - 1][i + 1][j], true);
        solve(cctvn + 1);
        for (int j = 0; j < fillcount; j++)
            observe(x, y, spec[cctvkind - 1][i + 1][j], false);
    }
}

int main(void) {
    scanf("%d %d", &N, &M);
    for (int i = 0; i < N; i++)
        for (int j = 0; j < M; j++) {
            scanf("%d", &map[i][j]);
            if (0 < map[i][j] && map[i][j] < 6)
                cctv[K][0] = i, cctv[K++][1] = j;
        }
    solve(0);
    printf("%d\n", ans);
    return 0;
}

 

다른 분들의 코드에선 5번 카메라는 회전에 관계없이 고정이므로 아예 처음에 감시 체크를 하고 중복 순열에서 제외시키는 모습을 볼 수 있었습니다. 확실히 5번은 회전에 따라 감시 해제할 필요가 없죠.

또, 각 카메라의 회전 방향만 별도의 배열에 저장하고 넘어간 뒤 모든 카메라의 회전 방향을 지정했을 때(중복 순열의 마지막에) 그 배열을 참조해서 감시 체크를 하고 답을 구하는 식의 코드도 있었습니다.

복기

처음엔 단순히 완전탐색 문제군 하고 시작했다가 몇 번이나 생각을 잘못해서 디버깅에 시간을 많이 허비했던 문제입니다. 그래도 다행히 잘못 생각한 부분들을 예제 입출력에서 다 잡아줘서 풀 수 있었네요. ㅋㅋ 처음 잘못 생각했던 건(사실 별 생각이 없었다는게 더 맞는 것 같지만) 동일한 거리의 물고기가 있을 때 먹을 물고기의 우선 순위(1번: 위에 있는 물고기, 2번: 왼쪽에 있는 물고기) 구현을 단순히 탐색 방향을 위, 왼쪽, 오른쪽, 아래 로 해놓고 됐다고 생각했던 건데요. 4번 예제에서 걸려서 디버깅 하다보니 중간에 잘못된 물고기를 먹더군요. 다시 생각해보니 아래 그림처럼 6번이 5번보다 위에 있어 우선 순위가 높지만 Queue엔 더 늦게 들어간다는걸 깨달았습니다.

두 번째 잘못 생각한 건 거리를 그냥 두 지점간 최단 거리로 구한건데 이러면 돌아가는 경우를 전혀 고려하지 않은게되죠. 변명을 해보자면...... 이때 많이 졸렸나봐요. 아래는 코드입니다.

#pragma warning(disable : 4996)
#include <stdio.h>
typedef struct _queue {
    int d[405][2];
    int f, r;
} Queue;
int N, M, sx, sy, ss, sc, ans;
int in[22][22], d[4][2] = { {-1, 0}, {0, -1}, {0, 1}, {1, 0} };
bool vis[22][22];
Queue q1, q2, q3;

void enqueue(Queue* q, int x, int y) {
    q->d[q->r][0] = x;
    q->d[q->r][1] = y;
    (q->r)++;
}
void dequeue(Queue* q, int* px, int* py) {
    *px = q->d[q->f][0];
    *py = q->d[q->f][1];
    (q->f)++;
}
bool isEmpty(Queue* q) {
    return q->f == q->r;
}
void clear(Queue* q) {
    q->f = q->r = 0;
}
void solve(void) {
    Queue *cur = &q1;
    Queue *next = &q2;
    enqueue(cur, sx, sy);
    vis[sx][sy] = true;
    
    int dis = 0;
    while (M > 0 && !isEmpty(cur)) {
        while (!isEmpty(cur)) {
            int x, y, xx, yy;
            dequeue(cur, &x, &y);
            for (int i = 0; i < 4; i++) {
                xx = x + d[i][0];
                yy = y + d[i][1];
                if (xx < 1 || N < xx || yy < 1 || N < yy || vis[xx][yy]) continue;
                if (in[xx][yy] > ss) continue;
                
                vis[xx][yy] = true;
                if (in[xx][yy] == 0 || in[xx][yy] == ss)
                    enqueue(next, xx, yy);
                else
                    enqueue(&q3, xx, yy);
            }
        }
        dis++;
        if (isEmpty(&q3)) {
            Queue* tmp = cur;
            cur = next;
            next = tmp;
            clear(next);
        }
        else {
            int x, y, xx, yy;
            dequeue(&q3, &x, &y);
            while (!isEmpty(&q3)) {
                dequeue(&q3, &xx, &yy);
                if (x > xx || (x == xx && y > yy))
                    x = xx, y = yy;
            }
            clear(cur);
            clear(next);
            clear(&q3);
            enqueue(cur, x, y);
            in[x][y] = 0;
            for (int i = 1; i <= N; i++)
                for (int j = 1; j <= N; j++)
                    vis[i][j] = false;
            vis[x][y] = true;
            ans += dis;
            dis = 0;
            sx = x, sy = y;
            sc--;
            if (sc == 0)
                ss = sc = ss + 1;
            M--;
        }
    }
}

int main(void) {
    scanf("%d", &N);
    for (int i = 1; i <= N; i++)
        for (int j = 1; j <= N; j++) {
            scanf("%d", &in[i][j]);
            if (in[i][j] == 9) {
                sx = i;
                sy = j;
                ss = sc = 2;
                in[i][j] = 0;
            }
            else if (in[i][j]) {
                M++;
            }
        }
    solve();
    printf("%d\n", ans);
    return 0;
}

N의 최댓값이 20이라 대충 계산해서 물고기 찾는데 맵 다뒤져도 400, 물고기가 있어봐야 400마리, 합쳐도 160K 번 밖에 안되서 일단 마음은 편했습니다. 코드가 좀 지저분하지만 설명을 해보자면 우선 아기상어는 입력받을 때 맵에서 때어내서 변수 sx, sy, ss, sc 에 넣어 두었습니다. (변수명이 abc라 부연설명을 붙히자면 앞에 s는 shark, 뒤의 s는 size, c는 count) 맵안의 물고기 갯수는 변수 M에 저장해두었고요. solve() 함수를 보면 구현 방법은 BFS입니다. 큰 while문 안에 작은 while문 하나와 if else문 하나가 들어있습니다. 큰 while문은 먹을 물고기가 있고 이동이 가능한 상태면 계속 돕니다. 작은 while문은 실제 이동을 하는 부분입니다. step-by-step으로 이동하기위해 큐를 하나 더 사용했습니다. 이번 턴에 움직일 위치는 cur 큐에 들어있고 다음 이동할 위치는 next라는 다른 큐에 넣었습니다. 또, 이동중에 먹을 수 있는 물고기를 발견하면 그 물고기는 q3이라는 또다른 큐에 넣었습니다. 이번 턴의 이동을 마치면(작은 while문이 끝나면) if else문에서 먹을 수 있는 물고기를 발견했는지? 못했는지? 확인합니다. 발견 못했으면 next큐에 들어있는 좌표들이 새로운 턴에서 이동을 시작할 위치가 되기 때문에 cur큐와 next큐를 서로 바꿉니다. 발견했다면 q3안에 있는 물고기중 가장 우선 순위가 높은 물고기를 찾아서 먹고 아기 상어의 위치를 바꾼 후 새로운 탐색을 위한 준비를 하고 시작점으로 돌아갑니다.

문제를 풀고 다른 훌륭한 분들의 코드를 보니...... 작은 while문을 시작하기 전에 cur 큐의 사이즈를 아니까 딱 그만큼만 돌리면 어차피 한 step만큼만 돌기 때문에 굳이 큐를 여러개 쓸 필요는 없었네요. 대신 나머지 연산으로 큐를 환형으로 만들고요. 먹을 물고기를 저장했던 q3도 그냥 발견할 때마다 그때그때 확인해서 가장 우선순위 높은 놈으로 들고있으면 될 것 같습니다. 허허,, 이 것으로 오늘의 복기를 마칩니다.

'개발 > c' 카테고리의 다른 글

[gstreamer] windows에서 설치 및 예제 빌드  (0) 2022.01.04
[백준] 15683 감시  (0) 2020.05.09
[백준] 17825 주사위 윷놀이  (0) 2020.04.24
[백준] 17822 원판 돌리기  (0) 2020.04.22
[백준] 3111 검열  (0) 2019.03.05

복기 


 숫자도 적고 딱봐도 시뮬레이션인 문제입니다. 딱! 보고 게임판에 숫자를 어떻게 저장할지 막막하기도 했고요. 특별한 방법은 생각나지 않았지만 파란색칸이 공통적으로 다섯 번에 한 번씩 있어서 '5로 나눠 떨어질 때 길을 바꿔주면 좋겠다.', '게임판을 이차원 배열에 저장하고 각 길을 하나의 행으로 표현하면 5로 나눈 몫을 보고 길을 바꿔줄 수 있겠다' 정도만 생각하고 맵을 이차원 배열로 선언했습니다....만 언제든지 지울 수 있는 상태였죠.


1
2
3
4
5
6
int map[5][35= {
    { 02468101214161820222426283032343638400, },
    { 10131619253035400, },
    { 0,  202224253035400, },
    { 30282726253035400, },
};
cs

2번째 길(20, 22, ...)만 다른 길과 길이가 달라서 중복체크를 쉽게하려고 앞에 0을 넣어 길이를 맞췄구요.


4개 말을 무작위로 10회 움직이는 경우는 4^10 = 1M 번이니까 완전 탐색을 해도 시간은 충분합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void play(int lvl, int sum) {
    if (lvl >= 10) {
        if (ans < sum)
            ans = sum;
        return;
    }
    for (int n = 0; n < 4; n++) {
        // 갈 수 있는지?
         // 없으면, continue;
         // 있으면, get 갔을 때 얻는 점수
        play(lvl + 1, sum + score);
    }
}
 
int main(void) {
    for (int i = 0; i < 10; i++)
        scanf("%d"&dice[i]);
    play(00);
    printf("%d\n", ans);
    return 0;
}
cs


 먼저, 큰 틀로 전체 탐색하는 코드만 작성해놨고 '갈 수 있는지 확인하는 부분만 따로 함수를 만들어봐야지' 했습니다. 몇 번째 플레이어가 갈 차례인지, 얼마나 갈건지를 입력으로 하고 갈 수 없으면 마이너스 값 반환, 갈 수 있으면 더할 점수를 반환하는 함수를 만들기로 했고요. 대강 윤곽만 잡아보니


1
2
3
4
5
6
7
8
9
10
11
12
13
14
int go(int n, int dis) {
 
    // 이미 도착한 플레이어? 이동 불가 return -1;
 
    // 현재 파란원? 길 바꾼다
 
    // 이동
 
    // 도착 ? return 0;
 
    // 이미 누가 있다면? 이동 불가 return -1;
 
    // 점수 반환 return map[player[n].x][player[n].y];
}
cs

이었고 이정도 쓰고 나선 막연하게 이거 되겠다하는 생각이 들었습니다.


코드

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#pragma warning(disable : 4996)
#include <stdio.h>
struct Player {
    int x, y;
};
struct Player player[5];
int dice[15], ans;
int map[5][35= {
    { 02468101214161820222426283032343638400, },
    { 10131619253035400, },
    { 0,  202224253035400, },
    { 30282726253035400, },
};
 
int go(int n, int dis) {
    struct Player npc = player[n];
    int end = 21;
 
    // blue point ? change road
    if (npc.x == 0 && npc.y < end) {
        int q = npc.y / 5;
        int r = npc.y % 5;
        if (q < 4 && r == 0) {
            npc.x = q;
            npc.y = q == 2;
        }
    }
    if (npc.x != 0)
        end = 8;
 
    // already end ? can't pick it up
    if (npc.y >= end
        return -1;
 
    // move
    npc.y += dis;
 
    // end ? return 0
    if (npc.y >= end) {
        player[n] = npc;
        return 0;
    }
 
    // has owner ? can't pick it up
    for (int i = 0; i < 4; i++) {
        if (i == n) continue;
        if (npc.x == player[i].x) {
            if (npc.y == player[i].y)
                return -1;
        }
        else if (npc.x > 0 && player[i].x > 0) {
            if (npc.y == player[i].y && npc.y >= 4)
                return -1;
        }
        else {
            int n1 = map[npc.x][npc.y];
            int n2 = map[player[i].x][player[i].y];
            if (n1 == n2 && n1 == 40)
                return -1;
        }
    }
 
    // moving alright ? move
    player[n] = npc;
    return map[npc.x][npc.y];
}
 
void play(int lvl, int sum) {
    struct Player save;
    int dis;
    if (lvl >= 10) {
        if (ans < sum)
            ans = sum;
        return;
    }
    for (int n = 0; n < 4; n++) {
        save = player[n];
        dis = go(n, dice[lvl]);
        if (dis < 0continue;
        play(lvl + 1, sum + dis);
        player[n] = save;
    }
}
 
int main(void) {
    for (int i = 0; i < 10; i++)
        scanf("%d"&dice[i]);
    player[0].y = dice[0];
    play(1, map[0][dice[0]]);
    printf("%d\n", ans);
    return 0;
}
cs


 반성할 점으로는 play() 함수에서 go() 함수를 들어가기 전과 후로 전역 변수 player가 달라지기 때문에 당연히 저장해두었다가 나왔을 때 복구해야하는데 이 부분을 생각하지 못하고 '왜 틀리지?' 하며 go() 함수 안만 들여다보느라 시간을 허비한 점이 있습니다. 항상 함수를 작성할 때는 입력과 출력이 무엇인지? 어떤 처리를 하는 함수인지? 함수 수행으로 변화되는 것이 무엇인지? 어떻게 사용할 것인지? 등을 객관적으로 생각해야하는데 함수 내부 구현에 집중하다보면 계속 놓치게 되는 것 같습니다. ㅜ_ㅜ;


보고나서 좋다고 생각했던 코드로는 아이디 goooora 님의 코드가 있습니다. (https://www.acmicpc.net/source/16404426)

코드를 보면 코드양도 적고 되게 직관적이라 코드 작성 시간이 굉장히 짧았을 것으로 추측됩니다. 복기끝.

'개발 > c' 카테고리의 다른 글

[백준] 15683 감시  (0) 2020.05.09
[백준] 16236 아기 상어  (0) 2020.04.28
[백준] 17822 원판 돌리기  (0) 2020.04.22
[백준] 3111 검열  (0) 2019.03.05
[백준] 4179 불!  (0) 2018.12.13

복기

 

그동안 나태한 나날을 보내다 오랜만에 푼 탓도 있겠지만 정말 시간을 많이 잡아먹은 문제입니다. (시간을 재진 않았지만 세 시간은 넘어간 것 같네요.)

또, 문제 푸는 방법을 다시 한 번 깨닫게 해 준 문제이기도 하죠.

 

  * 문제 푸는 방법
  1번 문제를 읽는다
  2번 문제를 이해한다.
  3번 문제를 푼다.

 

정말... 알지만 실천하기 힘든 것 같습니다. ㅠㅠ 문제를 읽기도 전에 이미 손은 자판을 치고 있고 이해는 대충한 상태로 코드를 짜다가 이상하다 싶을 때가 되서야 문제를 다시보게 되는... 항상 고쳐야하지 하고 다짐하지만 마음이 급한 탓인지, 성격이 급한 탓인지 쉽지가 않네요. 정직하게 가는 길이 지름길인 것을... -_-; 이번에도 T번의 입력만큼 회전을 모두 시키고 나서 원판의 인접한 수를 찾는 거라고 이해하고 코드를 짜다가 싸한 느낌에 문제를 다시 읽어보니 T번의 매 입력마다 원판 회전 후 인접한 수를 찾아야 하는거였죠.

 

흠... 글을 쓰기 전에는 문제 잘못읽어서 시간을 많이 날린줄 알았는데 쓰면서 다시 생각해보니 굳은 머리와 구현 능력이 부족함에도 좀 더 이쁘게 코드를 짜려했던 제 욕심이 범인이었네요.

 

우선 원판을 k번 회전시키는 부분에서부터 막혔는데 처음에 생각했던건 1번 숫자를 먼저 저장해놓고 M-1번 만큼 다음에 이번 자리에 올 숫자의 인덱스를 찾은 뒤 이동시키고 마지막엔 남는 자리에 저장해 두었던 1번 숫자를 넣어주려고 했었죠. 예를 들어, M이 5이고 원판 하나의 숫자가 { 1, 7, 3, 9, 5 } 일때, 반시계방향으로 2회 회전시켜야한다면 저장한 1번 인덱스에 올 인덱스는 3번이고 그다음은 5 < 2 < 4 그리고 M번째 마지막 4에는 저장해두었던 < 1 을 넣는것이죠.

네. 하지만 간과한 부분이 있었으니 경우에 따라 모든 숫자에 회전의 기회가 돌아가지 않을 수가 있었습니다. { 1, 2, 3, 4 } 에 CCW로 2회라면 1 < 3 < 1 < 3 < ... 이렇게 돌게 되는거죠. 결국 저대로라면 M과 k에 따라 숫자를 2개, 3개, 저장해야하는 꼴이니... 결국은 가장 쉽게 생각되는 배열 복붙으로 풀었습니다. 크기도 크지 않은데 배열을 하나 더 만들려니 왜이리 마음이 안좋은지 ㅠ.

회전시 시작 위치에(1번) 들어갈 인덱스가 3번이라면 3번부터 배열 한바퀴 돌리면서 복사한 후 나중에 그대로 붙혀넣기했습니다.

 

#pragma warning(disable : 4996)
#include <stdio.h>

int N, M, T;
int in[55][55];

void erase(int n, int m, int c) {
    if (in[n][m] != c) return;
    in[n][m] = 0;
    erase(n + 1, m, c);
    erase(n - 1, m, c);
    erase(n, (m - 1) ? m - 1 : M, c);
    erase(n, (m + 1) % (M + 1) ? m + 1 : 1, c);
}
int main(void) {
    scanf("%d %d %d", &N, &M, &T);
    for (int n = 1; n <= N; n++)
        for (int m = 1; m <= M; m++)
            scanf("%d", &in[n][m]);
    for (int t = 0; t < T; t++) {
        int x, d, k;
        scanf("%d %d %d", &x, &d, &k);
        k = d ? k : M - k;

	// 원판 회전시키기
        int cp[55] = {};
        for (int n = x; n <= N; n += x) {
            for (int m = 1; m <= M; m++)
                cp[m] = in[n][(k + m - 1) % M + 1];
            for (int m = 1; m <= M; m++)
                in[n][m] = cp[m];
        }

	// 회전 후 붙어있는 수 삭제
        int del = 0;
        for (int n = 1; n <= N; n++) {
            for (int m = 1; m <= M; m++) {
                if (in[n][m] == 0) continue;
                if (in[n][m] != in[n + 1][m] &&
                    in[n][m] != in[n - 1][m] &&
                    in[n][m] != in[n][(m - 1) ? m - 1 : M] &&
                    in[n][m] != in[n][(m + 1) % (M + 1) ? m + 1 : 1]) continue;
                del++;
                erase(n, m, in[n][m]);
            }
        }
        if (del) continue;

	// 평균내서 더하기 빼기하는 작업
        double avr = 0;
        int cnt = 0;
        for (int n = 1; n <= N; n++) {
            for (int m = 1; m <= M; m++) {
                avr += in[n][m];
                if (in[n][m]) cnt++;
            }
        }
        if (cnt != 0)
            avr /= cnt;
        for (int n = 1; n <= N; n++) {
            for (int m = 1; m <= M; m++) {
                if (in[n][m] == 0) continue;
                if (in[n][m] < avr) in[n][m]++;
                else if (in[n][m] > avr) in[n][m]--;
            }
        }
    }
    
    // 답구하기 총합
    int ans = 0;
    for (int n = 1; n <= N; n++)
        for (int m = 1; m <= M; m++)
            ans += in[n][m];
    printf("%d\n", ans);
    return 0;
}

더욱 정진해야겠습니다. :-)

'개발 > c' 카테고리의 다른 글

[백준] 16236 아기 상어  (0) 2020.04.28
[백준] 17825 주사위 윷놀이  (0) 2020.04.24
[백준] 3111 검열  (0) 2019.03.05
[백준] 4179 불!  (0) 2018.12.13
winpcap pcap_pkthdr 구조체에서 caplen과 len의 차이  (0) 2018.12.01

회사 사정상 갑작스럽게 안드로이드 를 하게됐습니다.

자바도 해본적이 없는데 안드로이드라니... 당황스럽지만 앞으론 안드로이드 공부를 해야합니다.

 

먼저 개발 환경을 찾아보니 대부분 안드로이드 스튜디오를 사용하는 것 같습니다.

다행히 설치는 공식 홈페이지에서 친절하게도 한글로 가이드되고 있습니다.

* 사이트 : https://developer.android.com/studio/install?hl=ko

 

Android 스튜디오 설치  |  Android 개발자  |  Android Developers

Windows, macOS 또는 Linux에서 Android 스튜디오를 설정 및 설치합니다.

developer.android.com

새로 시작하는 마음으로 빈 프로젝트를 만들었습니다.

아래에는 빈 프로젝트 생성 후 빌드하면서 겪었던 두 가지 문제에 대해서 적었습니다.

 

 

1. No target device found.

 

가상 디바이스를 설정하지 않아 발생한 에러입니다. 자세히 살펴보면 우상단에 No Device 라고 적힌 부분이 보입니다.

    "Open AVD Manager - Create Virtual Device - New Hardware Profile"

 

가지고 있는 핸드폰 공기계 사양과 비슷하게 하드웨어 프로파일을 만들었습니다.

이제 생성한 디바이스가 보입니다.

 

2. Error while waiting for device: Could not start AVD

참고

 

참고 링크 확인시 아래 내용을 확인할 수 있습니다.

> 가상화 확장 프로그램 요구사항

개발 환경 요구사항 외에도 컴퓨터 프로세서는 다음 가상화 확장 프로그램 기술 중 하나를 지원해야 합니다.

- Intel 가상화 기술(VT, VT-x, vmx) 확장 프로그램
- AMD 가상화(AMD-V, SVM) 확장 프로그램

순간 내 컴퓨터가 가상화 확장프로그램을 지원하지 않는 컴퓨터면 어떻하지 하는 걱정이 들었지만 다행히 최신 프로세서는 대부분 가상화 확장 프로그램을 지원한다고 합니다.

 

번거롭지만 컴퓨터를 재부팅하고 BIOS 메뉴에 들어가야합니다.

저의 경우 AMD사의 CPU를 사용합니다. 아래 경로에서 SVM을 켤 수 있었습니다.

    "오버 클럭 셋팅 메뉴" - "그외 CPU 셋팅" - "SVM Mode"

 

그리고, Intel의 "HAXM"대신 "Hyper-V"를 사용 합니다. 경로는

    "제어판" - "프로그램 및 기능" - "Window 기능 켜기/끄기" - "Hyper-V"

입니다.

이렇게 설정을 모두 마치면

빈프로젝트 실행시

"Hello World!"

문구를 확인할 수 있습니다. ㅎㅎ

'개발 > android' 카테고리의 다른 글

가로, 세로 Tab 만들기 예제  (0) 2020.06.04

+ Recent posts