제한은 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

복기

 

먼저 이 문제는 제가 풀지못한 문제입니다. 결국 다른 사람의 풀이를 보고 풀었죠...ㅜㅜ

 

구슬이 중간에서 터지면 위의 구슬이 떨어져서 밑이랑 색이 같으면 또 터지는 뿌요뿌요 게임처럼 삭제된 문자열 좌우 문자를 붙혀서 A 문자열이 나오면 또 삭제를 해줘야하는 점이 아마 이 문제를 까다롭게 만드는 점인 것 같은데요.

 

먼저, 아래는 다른 사람 풀이를 보고 작성한 코드입니다.

 

더보기

 

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
// 검열
#include <stdio.h>
#pragma warning(disable : 4996)
char A[35], T[300015], s1[300015], s2[300015], b;
int len, l, r, t1, t2;
 
char check(void) {
    if (0 == b) {
        if (t1 < len) return 0;
        for (int i = 1; i <= len; i++) {
            if (s1[t1 - i] != A[len - i]) {
                return 0;
            }
        }
    }
    else {
        if (t2 < len) return 0;
        for (int i = 1; i <= len; i++) {
            if (s2[t2 - i] != A[i - 1]) {
                return 0;
            }
        }
    }
    return 1;
}
int main(void) {
    scanf("%s %s", A, T);
    for (len = 0; A[len]; len++);
    for (r = 0; T[r + 1]; r++);
 
    while (l <= r) {
        if (0 == b) {
            s1[t1++= T[l++];
            if (s1[t1 - 1== A[len - 1]) {
                if (check()) {
                    t1 -= len;
                    b ^= 1;
                }
            }
        }
        else {
            s2[t2++= T[r--];
            if (s2[t2 - 1== A[0]) {
                if (check()) {
                    t2 -= len;
                    b ^= 1;
                }
            }
        }
    }
    b = 0;
    while (t2) {
        s1[t1++= s2[--t2];
        if (s1[t1 - 1== A[len - 1]) {
            if (check())
                t1 -= len;
        }
    }
    s1[t1] = 0;
    return !printf("%s\n", s1);
}// 1
cs

 

위 코드가 제가 맨처음 작성했던 코드와 다른 점은 비교를 앞에서부터 하는가 뒤에서부터 하는가 입니다.

 

글이 좀 애매한데 어떤 의미인가 하면

 

 
 A: aac


 T: aaaaaaccc

 

위의 입력이 주어졌을 때, T의 왼쪽에서부터 A 문자열과 일치하는 문자열을 찾는 상황에서

 

앞에서부터 비교한다는건 (a a c 순으로 비교) 아래와 같이 일치하는 문자열을 찾았을 때 인덱스가 4 가 되는 것이고

 

뒤에서부터 비교한다는건 (c a a 순으로 비교) 인덱스가 6 일 때 아래 상황이 되는 겁니다. 

 

 
 aaaa (aac) cc



별 것 아닌 차이 같지만 앞문자부터 비교하면 삭제후 다음 인덱스부터 앞방향 탐색 범위에 삭제 때문에 끊기고 뒤에 남은 문자들이 포함되지 못합니다. 추가로 뭔 짓거리를 더 해줘야 하는 셈이죠. : aaaa (c->) c

 

반면에 뒷문자부터 비교하면 삭제후 다음 인덱스로 옮겨도 버퍼 안의 내용과 비교하면 탐색하던 방향 그대로 뒤에 남은 문자들을 포함해서 비교할 수 있죠 : aaaa (<-c) c

 

물론 전자처럼 구현해도 관계는 없을 겁니다. 하지만, 확실히 후자가 쉬운 길인 것 같네요.

 

더보기
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
// 검열
#include <stdio.h>
#include <string.h>
#pragma warning(disable : 4996)
volatile char A[33], T[300015];
volatile int len, f, r;
 
struct Stack {
    char data[300015];
    int top;
} s1, s2;
void Push(Stack *ps, char in)    { ps->data[ps->top++= in;    }
char Pop(Stack *ps)                { return ps->data[--(ps->top)];    }
int GetCount(Stack *ps)            { return ps->top; }
 
int main(void) {
    scanf("%s %s", A, T);
    for (len = 0; A[len]; len++);
    for (r = 0; T[r]; r++);
    
    char bSame;
    int n;
    while (r - f >= len) {
        while (r - f >= len) {
            bSame = 1;
            for (int i = 0; i < len; i++) {
                if (T[f + i] != A[i]) {
                    bSame = 0;
                    break;
                }
            }
            if (bSame == 0
                Push(&s1, T[f++]);
            else {
                n = GetCount(&s1);
                n = n < len - 1 ? n : len - 1;
                f += len - n;
                for (int i = n - 1; i >= 0; i--)
                    T[f + i] = Pop(&s1);
            }
            while (GetCount(&s2) > 0 && r - f < len) {
                T[r++= Pop(&s2);
                T[r] = 0;
            }
            if (bSame == 1break;
        }
        while (r - f >= len) {
            bSame = 1;
            n = r - len;
            for (int i = 0; i < len; i++) {
                if (T[n + i] != A[i]) {
                    bSame = 0;
                    break;
                }
            }
            if (bSame == 0
                Push(&s2, T[--r]);
            else {
                n = GetCount(&s2);
                n = n < len - 1 ? n : len - 1;
                r -= len - n;
                T[r] = 0;
                for (int i = n; i > 0; i--)
                    T[r - i] = Pop(&s2);
            }
            while (GetCount(&s1) > 0 && r - f < len) 
                T[--f] = Pop(&s1);
            if (bSame == 1break;
        }
    }
    while (GetCount(&s1) > 0)
        T[--f] = Pop(&s1);
    while (GetCount(&s2) > 0)
        T[r++= Pop(&s2);
    T[r] = 0;
    return !printf("%s\n", T + f);
} // 2
cs

 

 

위 코드는 전자 방법으로 푼 코드이고 문자열 삭제후 뒤에 남은 문자랑 비교하려고 (A 문자열 길이 -1) 개 문자를 저장 버퍼에서 빼는... 쓰잘데기 없이 일을 만들어서 하는 방법입니다. 

 

그리고, 사실 이 코드는 틀린 코드입니다. 이유는 잘 모르겠지만 "출력 초과"로 나오네요.. ㅠ_ㅠ

 

'일단 머리좀 식히고 답을 잊을 때쯤 다시와서 틀린 이유를 찾아보자' 는 의미에서 이 코드도 남깁니다.

 

혹여 지나가던 고수님께서 이유를 집어 주신다면 감사할 따름이고요 ㅎㅎ.

 

복기끝

 

 

사실 지금 든 생각이지만 이 것도 시작 문자 한 개만 버퍼랑 비교하고 나머지는 T랑 비교하면 전자나 후자나 다를게 없겠다는 생각이 드네요............. 

지금까지 쓴 글이... ++'

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

[백준] 17825 주사위 윷놀이  (0) 2020.04.24
[백준] 17822 원판 돌리기  (0) 2020.04.22
[백준] 4179 불!  (0) 2018.12.13
winpcap pcap_pkthdr 구조체에서 caplen과 len의 차이  (0) 2018.12.01
ffmpeg 라이브러리 사용법  (2) 2018.11.16



복기



이 전에 이와 비슷한 문제를 푼 적이 있었고 풀면 풀리겠다 생각했습니다. 


네... 방심을 했던거죠. 저의 전략은 이랬습니다.


일단, 불을 BFS로 먼저 퍼뜨립니다.


이 때, 맵의 각 칸에 언제 불이 도달하는지에 대한 값을 써넣습니다.


이후 지훈이를 큐에 넣고 똑같이 BFS를 돌립니다.


큐안에 들어간 지훈이는 현재의 시간값?!을 가졌기 때문에 주위 칸에 도착했을 때


이 칸이 이미 옛날 옛적에 불에 뒤덮힌 칸인지, 아직 불이 오지 않은 칸인지 알 수 있습니다.



사실 위와 같은 풀이는 방문 여부를 check하는 배열을 맵으로 대신해서 메모리를 아껴보고자 하는 욕심에서


비롯된 풀이였습니다만, 이때 저는 지훈이의 방문을 check하지 않는 실수를 범합니다.


지훈이가 시간 값을 가지기 때문에 불이 일찍이 덮친 칸들로 이동하지 못하고 (큐에 들어가지 못하고)


루프가 종료될 것으로 생각했습니다.



하지만, 불이 애초에 가지 못한 곳은 지훈이가 이동하면서 check해주지 않으면 영원히 0의 값을 가지게 됩니다.


큐가 터지게 되는거죠...


런타임 에러를 보고도 여기까지 생각하지 못하고 뭐지? 하며 황당했던 지난 날의 나를 용서합니다. ㅋ


결국 답답함을 이기지 못하고 다지우고 visit 배열을 사용해서 문제를 풀었고


다시 푸는 과정에서 위와 같은 케이스, 예를 들면, 5 5 에서


 #

 #

 #

 #

 #

 #

 .

 .

 .

 #

 #

 .

 J

 .

 #

 #

 .

 .

 .

 #

 #

 #

 #

 #

 #

 

이와 같이 입력이 주어져 지훈이가 벽 안에서 계속 싸돌아 다니게 되는... 경우를 생각하게 됐습니다.


이후 지훈이의 방문 확인도 맵을 이용하면서 결국 visit 배열 메모리를 아껴서 문제를 다시 풀었습니다.


오기로 풀었습니다. ㅋㅋ




코드
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
#include <stdio.h>
 
struct Crd { int x, y; };
int in[1010][1010];
Crd q[1000010];
int f, r;
int main(void) {
    int R, C, sx, sy, x, y, xx, yy, i, j;
    char ct;
    scanf("%d %d"&R, &C); getchar();
    for (i = 0; i < R; i++) {
        for (j = 0; j < C; j++) {
            ct = (char)getchar();
            if (ct == 'J') sx = i, sy = j;
            else if (ct == '#') in[i][j] = -1;
            else if (ct == 'F') in[i][j] = 1, q[r].x = i, q[r++].y = j;
        }
        getchar();
    }
 
    while (f < r) {
        x = q[f].x, y = q[f++].y;
 
        for (i = 0; i < 4; i++) {
            xx = x + "1210"[i] - '1';
            yy = y + "2101"[i] - '1';
 
            if (xx < 0 || R <= xx || yy < 0 || C <= yy || in[xx][yy]) continue;
            in[xx][yy] = in[x][y] + 1;
            q[r].x = xx, q[r++].y = yy;
        }
    }
 
    f = r = 0;
    q[r].x = sx, q[r++].y = sy;
    in[sx][sy] = 1;
    int ans = 0x7fffffff;
 
    while (f < r) {
        x = q[f].x, y = q[f++].y;
        if (x == 0 || y == 0 || x == R - 1 || y == C - 1) {
            ans = in[x][y];
            break;
        }
 
        for (i = 0; i < 4; i++) {
            xx = x + "1210"[i] - '1';
            yy = y + "2101"[i] - '1';
 
            if (xx < 0 || R <= xx || yy < 0 || C <= yy) continue;
            if (in[xx][yy] && in[xx][yy] <= in[x][y] + 1continue;
            in[xx][yy] = in[x][y] + 1;
            q[r].x = xx, q[r++].y = yy;
        }
    }
 
    if (ans == 0x7fffffffprintf("IMPOSSIBLE");
    else                   printf("%d", ans);
    return 0;
}
cs


복기 끝.

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

[백준] 17822 원판 돌리기  (0) 2020.04.22
[백준] 3111 검열  (0) 2019.03.05
winpcap pcap_pkthdr 구조체에서 caplen과 len의 차이  (0) 2018.12.01
ffmpeg 라이브러리 사용법  (2) 2018.11.16
ffmpeg 이란?  (1) 2018.11.02

+ Recent posts