Game Develop

[Algorithm]Baekjoon 17144번 :: 미세먼지 안녕! 본문

Algorithm/Baekjoon

[Algorithm]Baekjoon 17144번 :: 미세먼지 안녕!

MaxLevel 2022. 12. 21. 23:13

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

 

17144번: 미세먼지 안녕!

미세먼지를 제거하기 위해 구사과는 공기청정기를 설치하려고 한다. 공기청정기의 성능을 테스트하기 위해 구사과는 집을 크기가 R×C인 격자판으로 나타냈고, 1×1 크기의 칸으로 나눴다. 구사

www.acmicpc.net

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#define MAX_NUM 0x3f3f3f3f
 
struct Node
{
    int y;
    int x;
    int dustAmount;
};
 
 
int r, c, t;
int arr[52][52= { 0 };
int dir[4][2= { {-1,0}, {1,0}, {0,-1}, {0,1} };
int ccwDir[4][2= { {0,1},{-1,0}, {0,-1},{1,0} };
int cwDir[4][2= { {0,1}, {1,0}, {0,-1},{-1,0} };
 
Node upper; // 공기청정기 위쪽
Node lower; // 공기청정기 아래쪽
 
 
void diffuse()
{
    queue<Node> q;
 
    for (int i = 1; i <= r; i++)
    {
        for (int j = 1; j <= c; j++)
        {
            if (arr[i][j] >= 1)
            {
                q.push({ i,j,arr[i][j] });
            }
        }
    }
 
    while (!q.empty())
    {
        int curY = q.front().y;
        int curX = q.front().x;
        int curDustAmount = q.front().dustAmount;
 
        q.pop();
 
        for (int i = 0; i < 4; i++)
        {
            int nextY = curY + dir[i][0];
            int nextX = curX + dir[i][1];
 
            if (arr[nextY][nextX] == MAX_NUM) continue;
            if (arr[nextY][nextX] == -1continue
 
            arr[curY][curX] -= curDustAmount / 5;
            arr[nextY][nextX] += curDustAmount / 5;
        }
    }
}
 
void clean()
{
    int prevNum = 0;
    int curY = upper.y;
    int curX = upper.x + 1;
    int dirIndex = 0;
 
    while (arr[curY][curX] != -1// 반시계방향
    {
        int temp = arr[curY][curX];
        arr[curY][curX] = prevNum;
        prevNum = temp;
 
        if (arr[curY + ccwDir[dirIndex][0]][curX + ccwDir[dirIndex][1]] == MAX_NUM)
        {
            dirIndex++;
        }
         
        curY += ccwDir[dirIndex][0];
        curX += ccwDir[dirIndex][1];
    }
 
    prevNum = 0;
    curY = lower.y;
    curX = lower.x + 1;
    dirIndex = 0;
 
    while (arr[curY][curX] != -1// 시계방향
    {
        int temp = arr[curY][curX];
        arr[curY][curX] = prevNum;
        prevNum = temp;
 
        if (arr[curY + cwDir[dirIndex][0]][curX + cwDir[dirIndex][1]] == MAX_NUM)
        {
            dirIndex++;
        }
 
        curY += cwDir[dirIndex][0];
        curX += cwDir[dirIndex][1];
    }
 
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
 
    cin >> r >> c >> t;
 
    int check = false;
    memset(arr, 0x3fsizeof(arr));
 
    for (int i = 1; i <= r; i++)
    {
        for (int j = 1; j <= c; j++)
        {
            cin >> arr[i][j];
 
            if (arr[i][j] == -1)
            {
                if (!check)
                {
                    upper = { i,j };
                    check = true;
                }
                else
                {
                    lower = { i,j };
                }
            }
        }
    }
 
    for (int i = 0; i < t; i++)
    {
        diffuse();
        clean();
    }
 
    int result = 0;
 
    for (int i = 1; i <= r; i++)
    {
        for (int j = 1; j <= c; j++)
        {
            if (arr[i][j] >= 1)
            {
                result += arr[i][j];
            }
        }
    }
 
    cout << result;
}
cs

 

이 문제에서는 먼지가 확산하는 단계에서 헷갈리지 않는게 가장 중요하다.

순차적으로 확산하는게 아니라, 한번에 확산하는 것이다.

즉 0,0에서 확산이 된다면 인접한공간에 먼지가 추가될 텐데, 그 추가된것을 합한다음 퍼뜨리면 안된다.

 

0,0이 15이고 0,1은 5라고 가정하다.

0,0에서는 0,1로만 확산할 수 있다고 또 가정한다.

 

그러면 0,0에서 먼저 확산처리를 한다면 0,0은 12가 될 것이고 0,1에 +3만큼 퍼질것이다.

그러면 0,1이 5+3이 되서 8이 될텐데, 여기서 0,1의 확산처리를 할 때 이 8값을 기준으로 확산처리를 하면 안된다.

0,0에서의 확산으로인한 +3이 추가되기 '이전'인 5를 기준으로 확산처리해야한다.

즉, 인접한곳에서 확산된 먼지로인한 누적값은 마지막에 합산을 해야하고, 확산처리를 할 땐 초기배열값으로 하면 된다고 생각해야 한다.

 

그렇기 때문에 먼저 각 배열의 노드를 큐에 담아서 기존의 값을 보관해준다. (반드시 보장해줘야 한다.)

그런다음 큐에서 노드를 하나씩 꺼내서 주변에 먼지를 뿌린 후, 해당 노드의 위치에 해당하는 arr위치에는 뿌린값만큼 -를 해주고 뿌린 위치에 해당하는 arr위치에는 합연산을 해준다.

 

뿌리는 값에 대한 기준은 미리 큐에 보관했던 기존의 값으로 뿌리는 것이기 때문에 arr에 바로 적용해도 된다.

 

이후 공기청정기 정화작업을 해줘야하는데 나같은 경우 반시계방향의 dir배열(ccwDir)과 시계방향의 dir배열(cwDir)을 만들어줘서 방향을 꺾으면서 탐색했다.

 

이건 약간 미세팁인데, 배열을 만들 때 행과 열에 +2씩 해서 한겹을 더 둘러싼 다음, 해당 한겹의 원소값들에는 쓰레기값들을 넣어놓으면 배열의 유효범위체크할 때 편하다.