제한은 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번은 회전에 따라 감시 해제할 필요가 없죠.

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

저의 경우 취직하고 부모님 집하고 회사간 거리가 왕복 5시간 정도였고 교통비와 시간을 생각하면 고시원이라도 들어가는게 낫겠다 싶어서 고시원 생활을 시작했습니다. 처음들어간 고시원에서 10개월 정도 살았고 중간에 근무지가 바뀌면서 다른 고시원으로 옮겨서 8개월 가량을 더 살았습니다. 이 글은 약 1년 반의 고시원 생활을 마치고 작성하는 추억팔이 겸 후기입니다. 제가 살았던 시기는 2018년 7월부터 2019년 12월까지 입니다.

고시원의 장점은 몇 백 몇 천 하는 보증금이 필요없고 관리비나 공과금이 들어가지 않으며 다달이 월세만 내면 된다는 겁니다. 또, 언제든 원할 때 쉽게 방을 뺄 수 있습니다. 단점은 낯선 사람들과 세탁기, 화장실, 샤워실 등을 같이 써야하고 좁고 다닥다닥 붙은 방에서 산다는게 불편할 수 있다는 겁니다. 시설이 낙후되어 지저분한 곳도 있습니다. 하지만 이제 막 사회생활을 시작해서 모아둔 돈이 없을 때 들어갈 수 있는 곳은 고시원 밖에 없지않나 싶습니다 ㅠㅠ.

고시원의 한 달 월세는 22만원부터 45만원까지 다양합니다. 가격은 고시원의 전체적인 청결도나 제공하는 물품과 내부 시설에 따라 달라집니다. 제공 물품에는 밥이나 김치, 라면, 휴지, 세제 등이 있습니다. 시설에는 방의 넓이, 옷장, 옷걸이 봉, 책상, 냉장고, 내창, 외창 여부와 방 내부에 화장실이 있는지 등이 있습니다. 내창은 복도와 연결된 창문이고 외창은 밖과 연결된 창문입니다. 보통 다른건 없어도 옷장, 옷걸이 봉, 책상은 있습니다. 그만큼 없으면 불편합니다. 30만원 중후반에서 40만원대의 비싼 방은 대부분 더 넓고 안에 샤워가 가능한 화장실이나 에어컨이 있습니다. 도저히 공용 화장실, 샤워실을 못쓰겠다 하시는 분은 조금 더 금액을 내더라도 이런 방을 선택할 수 있습니다. 하지만, 시설에 따라서 물이 잘안내려가고 역류한다던지 하수구 냄새가 올라오거나 관리하기에 따라서 곰팡이가 생길 수 있다고 하니 잘 알아보고 선택하시길 바랍니다.

 

저는 화장실이나 샤워실을 공용으로 쓰는데 전혀 불편함을 느끼지 않아서 비싼 방은 선택지로 두지 않았습니다. 어차피 돈도 없었습니다 ㅋㅋ. 먼지랑 냄새에 예민한 편이라 외창이 있는방을 구했고 방을 구할 때 사장님과 흥정해서 2만원을 깎을 수 있었습니다. 그렇게 외창방 28만원으로 고시원 생활을 시작했습니다. 벌레도 없고 나름 깨끗한 고시원이었고 밥과 김치, 세제가 제공됐습니다. 저는 밥을 사먹고 들어가는 편이라 사는 동안 밥이나 김치는 전혀 먹지 않았지만 정수기와 세제는 있는동안 잘 썼습니다. 방 안에는 옷장, 옷걸이봉, 책상, 책꽂이, 냉장고, 랜선이 제공됐습니다. 외창방에는 단점이 하나있는데 겨울이 되면 굉장히 춥습니다. 창문이 냉기를 차단하지 못하기 때문입니다. 생긴게 원래 부실한 창문이라 방한지를 붙혀도 소용이 없었고 겨울엔 내창방으로 바꿨습니다. 값은 26만원으로 줄었고 살다보니 외창없어도 괜찮아서 있는동안 계속 그 방에서 살았습니다. 첫번째 고시원에선 살면서 방 사진을 찍어둔게 없어서 고시원 홈페이지에 있는 사진을 가져왔습니다.

 

두 번째로 살게된 고시원은 그 지역에서 고르고 골랐지만 시설이 좋지 않았습니다. 1층과 2층이 있었는데 1층은 전체적으로 습하고 잠깐 봤는데도 바퀴벌레가 많이 보여서 2층으로 골랐습니다. 외창방으로 구했고 24만원에 들어갔습니다. 공용 정수기와 냉장고가 있었고 방 안에는 옷장, 책상, 랜선이 있었습니다.

옷걸이 봉은 없어서 근처 생활용품점에서 사서 달았습니다. 봉은 사진처럼 빨래후 건조대 또는 옷걸이용으로 사용하며 굉장히 편리합니다. 여름에 모기가 너무 많아서 잡아도 소용이 없다면 모기장을 추천합니다. 약간 아늑한 느낌도 들고 꿀잠을 잘 수 있습니다. 사진의 방은 여름에 비가 많이오면 천장에서 비가 샙니다. 주말에 부모님 집에 갔다왔더니 방이 물바다가 되서 기겁을 했던 기억이 나네요. 노트북 말리고 물퍼내고 방 건조시키느라 힘들었었죠.

여름엔 샤워하러 들어가면 곱등이가 벽이나 바닥에서 기다리고 있는데 먼저 건드리지않고 무시하고 샤워하면 튀어 오르거나 하지않습니다. 전 쫄보라 해보지 않았지만 뜨거운 물을 한 바가지 뿌리고 씻는 방법도 있다고 하네요. 세탁기도 관리가 안된건지 빨고나면 퀘퀘한 냄새도 나고 뭔지 모를 검갈색 먼지같은것도 묻어나오고 했었죠... 참.. 눈물이 앞을 가렸던 시절입니다 ㅎㅎ.

 

처음 고시원에 살게 되면서 친구 소개로 타인은 지옥이다라는 웹툰을 보고 겁도 많이나고 걱정도 했지만 살아보면 적응도되고 다 사람 사는 곳입니다. 다만 너무 싼 곳은 시설이 안좋고 너무 비싼 곳은 비싸니 자신의 적응력과 주머니 사정을 생각해서 적당한 고시원을 선택하시길 바랍니다. 물론, 살려는 지역에 고시원이 몇군데 없어서 선택의 폭이 좁아지는건 어쩔 수 없지만요. 애지간하면 부모님 집에서 출퇴근합시다. 집나가면 개고생이란 말은 맞는 말입니다. :)

평상시 밥먹다가 혀나 볼을 많이 깨무는 편이긴한데 이번엔 좀 세게 깨물었는지 많이 쓰라렸습니다. 보통 넘어지거나 어디 쓸려서 살 껍데기가 한 쪽은 붙어있고 한 쪽은 떠있는 경우가 있는데 혀 살 껍데기가 그렇게 됐죠... ;_; 아래는 살 껍데기가 떨어진 사진입니다. 밥을 몇 번 먹었더니 떨어졌네요.

깨물고 이틀 후

시간 좀 지나면 낫겠지 싶어서 되도록 반대편으로 밥먹고 조심하면서 지냈는데 일주일이 지나도 나아질 기미가 안보였습니다. 쓰라리니까 계속 신경쓰이고 신경쓰니까 더 아파지는 것 같더라고요.

깨물고 일주일 후

그래서 쫄보인 저는 바로 집에서 가까운 병원에 갔습니다. 찾아보니 혀 깨물었을 땐 이비인후과에 가라고 하더군요. 병원에선 눈으로만 딱 보더니 이상없이 잘 낫고 있다고 소독 한 번 해주고 끝났네요. 병원비는 9,400원, 가글 1,800원 해서 총 11,200원 나왔습니다. 의사 선생님 말로는 혀가 원래 회복하는데 시간이 오래 걸린다고 합니다. 깨문 정도에 따라 다르겠지만 보통 3주까지는 기다려 본다고 하네요. 3주가 지나도 회복이 안되면 조직 검사등 확인을 해야한다고 하니 알아두시면 좋을 것 같습니다.

편도염, 인두염 및 구강 염증 치료제 탄툼액 가글

약은 가글형으로 병원갔다오자마자 딱 한 번 사용했는데 하루가 지난 지금은 다 나은 건지 통증이 전혀 느껴지지 않습니다. 가글했다고 안 아픈건진 확실하지 않지만 어쨋던 통증이 없으니 좋네요. 3주가 지나진 않았지만 통증으로 신경이 쓰이신다면 약국에서 약만 구매해서 사용하셔도 좋을 것 같습니다.

복기

처음엔 단순히 완전탐색 문제군 하고 시작했다가 몇 번이나 생각을 잘못해서 디버깅에 시간을 많이 허비했던 문제입니다. 그래도 다행히 잘못 생각한 부분들을 예제 입출력에서 다 잡아줘서 풀 수 있었네요. ㅋㅋ 처음 잘못 생각했던 건(사실 별 생각이 없었다는게 더 맞는 것 같지만) 동일한 거리의 물고기가 있을 때 먹을 물고기의 우선 순위(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

일전에 동생이 쓰고있는 노트북이 안좋다며 투덜거린적이 있었죠. 80만원 정도선에서 데스크탑으로 사준다 했더니 후진거 안산다며 됐다던 녀석이었습니다만 ㅋㅋ.. 이번에 구입한 제 컴퓨터가 좋아보였던걸까요? 자기것도 맞춰달랍니다 ㅋ

 

제 컴퓨터와 비슷한 사양으로 샀는데 그사이 가격이 좀 내렸더군요 ㅠㅠ 항상 안사면 오르고 사고나면 내리는 법이죠.

 

구매 내역

 

가격이 내린 덕분에 이번엔 CPU 쿨러를("잘만 CNPS9X OPTIMA WHITE LED") 구매했는데도 가격은 저번보다 2만원가량 낮습니다.

 

이번에도 케이스는 3RSYS의 L530 입니다. 이번엔 화이트 색상인데 이유는 모르겠지만 화이트가 블랙보다 좀 더 비싸네요. 그래픽카드도 하얀색이 검은색보다 비싸던데 하얀색 찾으시는 분들이 많은가 봅니다. (저는 검은색을 선호하지만)

 

기본 CPU 쿨러와 다르게 잘만 쿨러는 메인보드에 부착된 기본 브라켓?을 사용합니다. 걸쇠를 걸고 나사를 조여 고정하는 방식이죠. 고정하고 나서 펜을 다는데 좀 어려움이 있었는데요. 아래 그림을 보면 펜을 걸수있도록 홈이 여기저기 많이 있습니다. 저는 펜에 고무 댐퍼를 부착해서 2번 위치에 펜을 걸었는데요. 인터넷에 글 찾아보면 보통은 고무 댐퍼 안붙히고 1번 위치에 많이들 거시는 것 같더라고요.

저번엔 인생 첫 컴퓨터 조립이었다보니 선정리가 좀 엉망이었는데 이번엔 똑같은 케이스로 두 번째 조립인데다 유튜브에서 좋은 동영상을 찾아서 보면서 하다보니 뒷면의 선이 전보다 많이 깔끔해졌습니다. 왠지 앞면이 조금 지저분해보이는 것 같지만요 ㅋㅋ. 조립하고 보니 하얀색도 멋있네요. 점점 컴퓨터 조립이 재미있어 지는 것 같습니다.

'OLD > 생활' 카테고리의 다른 글

중소기업청년전세대출 후기  (0) 2020.05.19
고시원 생활 후기  (3) 2020.05.06
혀 깨물어서 병원간 후기  (0) 2020.04.29
지방종 제거 수술 후기  (2) 2020.04.07
[구매 후기] 80만원대 데스크탑 컴퓨터  (0) 2020.03.27

2020년 03월 21일 지방종 제거 수술.

 

어느날 밤 샤워를 하다가 왼쪽 팔 접히는 부위에 혹을 발견했습니다. ㄷㄷ

검색해보니 지방종, 육종, 악성 종양, 양성 종양 등등 말이 많습니다.

 

정확한 확인을 위해 동네 병원에 방문했습니다.

의왕역 앞에있는 송재국 정형외과입니다.

 

엑스레이를 찍고 뼈엔 이상이 없는 걸 확인한 후 초음파 검사를 했습니다. 일단 지방종으로 보인다고 합니다. 제거를 해도되고 당장은 두고봐도 된다고 했지만 지방종이 생겼다는걸 알게되니 이상하게 계속 신경이 쓰여서 그냥 제거하기로 했습니다.

 

먼저는 마취를 합니다. 마취는 환부 주변, 사방으로 조금씩 4, 5번 정도 한 것 같습니다. 그리고 째는데 이 때 통증이 있으면 있다고 말합니다. 마취를 다시 해줍니다. 수술 시간은 1시 36분 ~ 2시 15분으로 대략 40분 정도 걸렸습니다. 수술 후 대략 2주 동안은 수술 부위에 물이 닿으면 안된다고 알려줍니다. 소독후 방수 밴드를 붙히고 마지막으로 붕대를 감아줍니다.

보다 확실하게 알기 위해 때어낸 조직으로 조직 검사를 한다고 합니다. 검사 결과 운동을 해서 생긴 염증 조직이라는데 운동했다고 이런게 생기는줄은 처음 알았네요... 이후론 이틀에 한 번정도 방문해서 소독하고 밴드와 붕대를 갈게 됩니다. 2주후 실밥을 제거합니다.

금액은 총 167,000 원이 나왔습니다. 저의 경우 소독을 두 번하고 또 오란 얘기가 없어서 안 갔는데 이틀마다 한 번씩 가는게 맞는 것 같습니다. 실밥빼러 갔더니 왜이리 안왔냐며 바빴냐고 물어보시더라고요. 다 갔다고 치면 대충 20만원 안되게 나왔겠네요. 이상 후기를 마칩니다. 다른 지방종 증상을 보이시는 분께 도움이 되었으면 좋겠네요. 가장 중요한건 인터넷 찾아보는 것 보다 병원에 빨리 가는 것 같습니다. 의사 진찰이 더 정확하니까요. 우리 모두 건강합시다~!

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

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

 

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

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

* 사이트 : 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

2021년 11월 ~

    * SPU

2020년 04월 ~ 2021년 10월 (19개월)

    * 안드로이드를(java) 이용한 앱 유지보수

2019년 06월 ~ 2020년 03월 (10개월)

    * Qt QML을 이용한 앱 유지보수 및 개발

        * USBMusic 선행 개발 보조

        * VideoManual 유지 보수

2018년 07월 ~ 2019년 05월 (11개월)

    * MFC를 이용한 앱 유지보수 및 개발

        * 컨버터 앱(H.264 패킷 파일 → mp4 파일) 개발

        * 카메라 펌웨어 업데이트 앱 개발

        * FrameGrabber 유지보수

'OLD > 기록' 카테고리의 다른 글

유용한 링크 모음  (0) 2018.11.30
도미니언 기록지 1  (0) 2018.11.25

+ Recent posts