16234번 - 인구 이동
2024-02-20
#C++#백준
문제 풀이 시간 : 2시간
문제 요약
- •
NxN크기의 땅 한칸마다 나라가 존재한다.
- •
국경선을 공유하는 두 나라의 인구 차이가 L명 이상, R명 이하라면 두 나라는 연합이 된다.
- •
위의 조건에 해당하는 모든 나라가 연합이 되었다면 그 연합은 인구이동을 한다.
- •
인구 이동은 (연합의 총 인구 수)/(연합을 이루는 나라의 개수) 가 된다.
- •
인구이동이 더이상 이뤄지지 않을 때까지 반복하여 며칠 동안 인구 이동이 발생하는지 구해라
문제 풀이
이 문제는 bfs로 인접한 나라들의 인구를 비교하면서 풀면 간단하게 풀 수 있을 거라고 생각했다.
초기 코드
#include <iostream>
#include <cmath>
#include <queue>
#include <set>
using namespace std;
int n, l, r, ans = 0;
int city[51][51], dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
bool visited[51][51];
queue<pair<int, int>> q, q2, ali;
set<pair<int, int>> s;
bool check(int x, int y) {
if (x < 1 || x > n || y < 1 || y > n)
return false;
return true;
}
void find(int x, int y) {
q.push({x, y}); //bfs탐색을 위한 큐
ali.push({x, y}); //연합 확인을 위한 큐
int people = city[x][y]; //연합의 인구 수
visited[x][y] = true;
while (!q.empty()) {
x = q.front().first;
y = q.front().second;
q.pop();
for (int i = 0; i < 4; i++) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if (check(tx, ty)) { //칸을 넘기지 않고
if (!visited[tx][ty]) { //방문되지 않았다면
int sum = abs(city[x][y] - city[tx][ty]); //인접한 국가의 인구 수 차이
if (sum >= l && sum <= r) { //조건에 만족한다면
ali.push({tx, ty}); //연합에 추가
q.push({tx, ty}); //bfs큐에 추가
visited[tx][ty] = true; //방문처리
people += city[tx][ty]; //연합 인구수 증가
} else {
s.insert({tx, ty}); //만족하지 않으면 방문되지 않은 나라를 저장하는 set에 저장
}
}
}
}
}
people = people / ali.size(); //연합의 각 나라의 인구 수 계산
while (!ali.empty()) { //각 나라의 인구수 저장
city[ali.front().first][ali.front().second] = people;
ali.pop();
}
if (!s.empty()) { //연합에 포함되지 않았던 나라들 다시 bfs
ans++;
while (true) {
if (s.empty())
break;
x = (*s.begin()).first;
y = (*s.begin()).second;
s.erase(s.begin());
if (!visited[x][y])
break;
}
find(x, y);
}
}
int main() {
cin >> n >> l >> r;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> city[i][j];
}
}
int sum = 0;
while (true) {
sum++;
find(1, 1);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
visited[i][j] = false;
}
if (ans == n * n - 1)
break;
ans = 0;
}
cout << sum - 1;
}
위 코드도 바로 통과를 했다.
근데 아무리 생각해도 저렇게 비효율적으로 할 필요가 있을까?
라는 생각을 하고 다른 사람들은 어떻게 풀었나 한번 찾아봤더니
굳이 s라는 set를 사용하지 않고 방문되지 않은 나라를 탐색하는 방식으로 해도 된다.
나라 개수가 아무리 많아봤자 50x50=2500이므로 시간초과와는 거리가 멀다.
그래서 다시 코드를 수정해서 아래와 같은 결과를 얻을 수 있었다.
최종 코드
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
vector<pair<int, int>> v;
queue<pair<int, int>> q;
int n, l, r, sum = 0;
int city[51][51], dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
bool visited[51][51];
bool check(int x, int y) {
if (x < 1 || x > n || y < 1 || y > n)
return false;
return true;
}
void find(pair<int, int> input) {
q.push(input); //bfs탐색을 위한 큐
visited[input.first][input.second] = true; //방문 처리
while (!q.empty()) {
int x = q.front().first;
int y = q.front().second;
q.pop();
for (int i = 0; i < 4; i++) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if (!visited[tx][ty]) {
if (check(tx, ty)) {
int temp = abs(city[x][y] - city[tx][ty]);
if (temp >= l && temp <= r) {
v.push_back({tx, ty}); //연합 처리를 위한 벡터 저장
q.push({tx, ty});
visited[tx][ty] = true;
sum += city[tx][ty]; //인구수 계산
}
}
}
}
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin >> n >> l >> r;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
cin >> city[i][j];
}
bool flag = true;
int ans = 0;
while (flag) { //연합이 발생하지 않을 때까지 반복
flag = false;
for (int i = 1; i <= n; i++) { //모든 나라를 보며 방문되지 않은 경우 bfs실시
for (int j = 1; j <= n; j++) {
if (!visited[i][j]) {
v.push_back({i, j});
sum = city[i][j];
find({i, j});
}
if (v.size() > 1) { //벡터에 2개이상의 나라가 저장됐다면 연합 발생
flag = true;
for (int k = 0; k < v.size(); k++) {
city[v[k].first][v[k].second] = sum / v.size();
}
}
v.clear();
}
}
if (flag)
ans++;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
visited[i][j] = false;
}
}
cout << ans;
}