문제 링크입니다 https://www.acmicpc.net/problem/16234
16234번: 인구 이동
N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모든 나라는 1×1 크기이기 때문에, 모든 국경선은 정사각형 형태이다. 오늘부터 인구 이동이 시작되는 날이다. 인구 이동은 다음과 같이 진행되고, 더 이상 아래 방법에 의해 인구 이동이 없을 때까지 지속된다. 국경선을 공유하는 두 나라의 인구 차이가 L명
www.acmicpc.net
1. 인구 이동이 가능한 나라들을 bfs를 통해 찾고 인구 이동을 시킨다.
(단, 하루 동안 같은 나라가 두 번 인구 이동을 하지 않도록 주의한다)
2. 하루가 지났으면 인구 이동 횟수를 한 번 증가시키고 1번으로 돌아간다.
좀 더 구체적인 설명은 주석을 참고해주세요
[소스코드 + 주석]
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
|
// [백준] 삼성 SW 역량 테스트 기출 문제 : 인구 이동(16234)
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int MAX = 50 + 10;
struct p { // 좌표를 저장하기 위한 구조체
int y, x;
};
// 하루동안 인구 이동이 일어난 나라를 체크해줄 배열
// 같은 날 똑같은 나라가 두 번 인구 이동을 하면 안됨
bool visit[MAX][MAX];
int n, L, R, ret, map[MAX][MAX], mapCopy[MAX][MAX];
const int dy[] = { -1, 0, 1, 0 };
const int dx[] = { 0, -1, 0, 1 };
// 좌표를 인자로 받아서 p type으로 바꿔주는 함수
p make_p(int y, int x) {
p temp;
temp.y = y, temp.x = x;
return temp;
}
// 좌표를 인자로 받아서 그 나라부터 인구 이동이 일어날 수 있는 나라들을
// bfs를 통해 탐색하고 탐색이 끝나면 인구를 이동시켜주고 인구 이동이
// 일어났으면 true, 일어나지 않았으면 false를 반환하는 함수
bool bfs(int y, int x) {
bool check[MAX][MAX] = { 0, }; // 인구 이동이 일어날 수 있는지 체크하는 배열
queue <p> q;
visit[y][x] = true;
check[y][x] = true;
q.push(make_p(y, x));
int cnt = 1, sum = 0;
bool flag = false;
while (!q.empty()) {
p cur = q.front();
q.pop();
// 인구 이동이 일어날 수 있는 나라만 Queue에 push하므로 현재 인구를
// 누적하여 더해준다
int current = map[cur.y][cur.x];
sum += current;
for (int i = 0; i < 4; i++) {
int yy = cur.y + dy[i];
int xx = cur.x + dx[i];
int next = map[yy][xx];
if (check[yy][xx] || yy < 0 || yy >= n || xx < 0 || xx >= n) continue;
int temp = abs(next - current);
// 다음칸의 인구와 내 인구의 차이가 L이상 R이하라면
if (L <= temp && temp <= R) {
cnt++; // 나라의 수를 세어주고
flag = true; // 인구 이동이 일어났습니다
visit[yy][xx] = true;
check[yy][xx] = true;
q.push(make_p(yy, xx));
}
}
}
if (flag) {
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
// 인구 이동이 일어난 나라이면 값(인구수의 합 / 나라의 수)을 넣어준다
if (check[i][j]) mapCopy[i][j] = sum / cnt;
return true;
}
else {
// 인구 이동이 일어나지 않았으면 그 좌표는 미 방문 처리
check[y][x] = false;
visit[y][x] = false;
return false;
}
}
void solve(void) {
scanf("%d%d%d", &n, &L, &R);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
scanf("%d", &map[i][j]);
mapCopy[i][j] = map[i][j]; // 입력받은 땅 정보 복사
}
}
while (1) {
bool isUpdate = false; // 인구 이동이 일어났냐
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (!visit[i][j])
if (bfs(i, j)) isUpdate = true;
if (!isUpdate) break; // 더 이상 인구 이동은 일어나지 않는다
else {
ret++; // 인구 이동이 일어난 횟수 카운트
// 하루가 지났으므로 visit 초기화 및 하루가 지난 map으로 update
memset(visit, 0, sizeof(visit));
memcpy(map, mapCopy, sizeof(mapCopy));
}
}
printf("%d", ret);
}
int main(void) {
solve();
return 0;
}
|
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
93
94
95
96
97
98
99
100
101
|
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int MAX = 50 + 10;
struct p {
int y, x;
};
bool visit[MAX][MAX];
int n, L, R, ret, map[MAX][MAX], mapCopy[MAX][MAX];
const int dy[] = { -1, 0, 1, 0 };
const int dx[] = { 0, -1, 0, 1 };
p make_p(int y, int x) {
p temp;
temp.y = y, temp.x = x;
return temp;
}
bool bfs(int y, int x) {
bool check[MAX][MAX] = { 0, };
queue <p> q;
visit[y][x] = true;
check[y][x] = true;
q.push(make_p(y, x));
int cnt = 1, sum = 0;
bool flag = false;
while (!q.empty()) {
p cur = q.front();
q.pop();
int current = map[cur.y][cur.x];
sum += current;
for (int i = 0; i < 4; i++) {
int yy = cur.y + dy[i];
int xx = cur.x + dx[i];
int next = map[yy][xx];
if (check[yy][xx] || yy < 0 || yy >= n || xx < 0 || xx >= n) continue;
int temp = abs(next - current);
if (L <= temp && temp <= R) {
cnt++;
flag = true;
visit[yy][xx] = true;
check[yy][xx] = true;
q.push(make_p(yy, xx));
}
}
}
if (flag) {
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (check[i][j]) mapCopy[i][j] = sum / cnt;
return true;
}
else {
check[y][x] = false;
visit[y][x] = false;
return false;
}
}
void solve(void) {
scanf("%d%d%d", &n, &L, &R);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
scanf("%d", &map[i][j]);
mapCopy[i][j] = map[i][j];
}
}
while (1) {
bool isUpdate = false;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (!visit[i][j])
if (bfs(i, j)) isUpdate = true;
if (!isUpdate) break;
else {
ret++;
memset(visit, 0, sizeof(visit));
memcpy(map, mapCopy, sizeof(mapCopy));
}
}
printf("%d", ret);
}
int main(void) {
solve();
return 0;
}
|
cs |

'알고리즘 > BaekJoon' 카테고리의 다른 글
백준 14500 : 테트로미노 (0) | 2020.02.18 |
---|---|
백준 14889 : 스타트와 링크 (0) | 2020.02.18 |
백준 1018 : 체스판 다시 칠하기 (0) | 2020.02.18 |
백준 2503 : 숫자 야구 (0) | 2020.02.18 |
백준 6603 : 로또 (0) | 2020.02.18 |