백준의 확장게임(16920) 문제이다.
[ 문제풀이 ]
1) 문제를 보면, 성을 하나씩 확장해 나가야 하는 문제이다. 초기에는 1개 모든 플레이어가 한 개 이상의 성을 가지고 있
으며, 더 이상 확장을 할 수 없을 때 게임이 끝나게 된다.
본인은 이 문제를 각 성이 확장해 나가는 것을 BFS로 확장할 수 있는 칸 수 만큼 Count해주면서 순서대로 확장하는
방식으로 구현해 보았다.
따라서, 가장 초기의 성을 가지고 BFS 연산을 해야하기 때문에 입력 받을 때 성의 위치를 저장해주어야 한다.
이 부분을 위해서 본인은 Queue배열을 사용해 주었다. Queue[MAX]; 이렇게 !
Queue에서는 총 3개의 값을 관리해 주었다. (x, y) 좌표와 움직일 수 있는 거리. 여기서 움직일 수 있는 거리라는 것은
각 플레이어마다 확장할 수 있는 칸 수를 의미한다.
2) 그렇다면 BFS에서 해줘야 할 일을 알아보자. 사실, DFS로 구현했다면 매개변수로 Depth를 이용해서, 플레이어마다
확장할 수 있는 칸수를 쉽게 계산하면서 확장했을 텐데, BFS라서 살짝 헷갈리는 부분이 있었다.
BFS에서 2개의 Queue를 사용해주었다. 기존에 입력할 때 부터 사용했던 Queue와 BFS탐색을 할 때에는 NQ라는
Next_Queue를 하나 더 사용해 주었다. NQ를 사용한 이유는 성들의 효율적인 관리를 위함이다.
먼저 코드를 보고 설명을 이어서 보도록 하자.
bool BFS(int Idx)
{
bool Temp_Flag = false;
queue<pair<pair<int, int>, int>> NQ = Q[Idx];
while (NQ.empty() == 0)
{
int x = NQ.front().first.first;
int y = NQ.front().first.second;
int Cnt = NQ.front().second;
NQ.pop();
if (Cnt == Move_Area[Idx]) Q[Idx].pop();
for (int i = 0; i < 4; i++)
{
int nx = x + dx[i];
int ny = y + dy[i];
int nCnt = Cnt;
if (nCnt == 0) break;
if (nx >= 0 && ny >= 0 && nx < N && ny < M)
{
if (MAP[nx][ny] == '.')
{
MAP[nx][ny] = Idx + '0';
Count_Area[Idx]++;
Q[Idx].push(make_pair(make_pair(nx, ny), Move_Area[Idx]));
NQ.push(make_pair(make_pair(nx, ny), nCnt - 1));
Temp_Flag = true;
}
}
}
}
return Temp_Flag;
}
이게 본인의 BFS부분이다. 가장 먼저 보면, NQ에 Q를 그대로 옮겨주는 과정이 있다.
그렇다면 여기서 NQ와 Q의 역할에 대해서 확실히 알아보고 넘어가자.
입력 때 부터 사용한 Queue(본문 : Q[Idx]) 는 현재 Idx번 플레이어의 확장 될 성들만 저장해 놓는 것이다.
예를 들어서, 1번 플레이어가 성을 확장한번 한 후에, 한 바퀴를 돌고 다시 1번 플레이어가 확장할 차례로 돌아왔다고 생각해보자.
그렇다면 여기서, 1번 플레이어가 초기에 가지고 있는 성들부터 다시 일일이 확장해 나가야 할 필요가 있을까?? 아니다. 그건 오히려
시간 낭비일 것이다. 이미, 한번 확장을 했기 때문에, 이 다음에 확장할 때에는 '확장된 성들'에서 확장을 시키면 되는 것이다.
즉, 이 새롭게 '확장된 성들'을 저장해 놓는 큐가 Queue이다.
그렇다면 Next_Queue(본문 : NQ)는 무엇일까 ??
그냥 간편한 연산을 위한것이다. 아래 그림을 한번 생각해보자.
이런 그림이 있다고 생각해보자.
플레이어1 이 2칸씩 확장 가능한 상태라고 생각해보면 빨강칸은
1칸 확장했을 때 갈 수 있는 곳, 파랑색은 2칸 확장했을 때 갈 수 있는 곳을
의미한다. 즉, 플레이어1의 자신의 턴이 한바퀴 돌고 돌아오게 된다면
저 그림에서 빨강칸 + 파랑칸에 대해서만 확장을 해주면 되는 것이다.
가장 초기인 (0, 0)에 대해서는 확장을 해줄 필요가 없다.
따라서, 위의 코드 중간에 보면
if(Cnt == Move_Area[Idx]) Q[Idx].pop(); 이라는 구문이 있는데
이 구문이 이미 확장한 원래의 성을, Queue에서 pop해주는 것이다.
NQ는 이렇게 성을 관리하기 위해서 사용해준 것이다.
3) 그렇다면 이제 마무리 단계만 남았다.
우리는 모든 플레이어가 더 이상 성을 확장할 수 없을 때까지 이 과정을 반복해줘야 한다.
따라서 본인은, Flag[]라는 bool형 배열을 만들어서, x번플레이어가 확장할 수 없는 경우 Flag[x] = false 로
표시해주었고, Flag[모든플레이어] = false가 되는순간 무한루프를 종료할 수 있도록 무한루프 안에서
실행시켜 주었다.
[ 소스코드 ]
[ 소스코드 ]
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define endl "\n"
#define MAX 1000
#define P_MAX 10
using namespace std;
int N, M, P;
int Move_Area[P_MAX];
int Count_Area[P_MAX];
bool Visit[MAX][MAX];
bool Flag[P_MAX];
char MAP[MAX][MAX];
queue<pair<pair<int, int>, int>> Q[P_MAX];
int dx[] = { 0, 0, 1, -1 };
int dy[] = { 1, -1, 0, 0 };
void Input()
{
cin >> N >> M >> P;
for (int i = 1; i <= P; i++) cin >> Move_Area[i];
for (int i = 0; i < N; i++)
{
for (int j = 0; j < M; j++)
{
cin >> MAP[i][j];
if ('1' <= MAP[i][j] && MAP[i][j] <= '9')
{
Q[MAP[i][j] - '0'].push(make_pair(make_pair(i, j), Move_Area[MAP[i][j] - '0']));
Count_Area[MAP[i][j] - '0']++;
} // 이 Queue의 의미 = Idx번 플레이어의 성은 (i, j)에서 Move_Area[MAP[i][j])만큼 확장해 나갈 수 있습니다.
}
}
memset(Flag, true, sizeof(Flag));
}
bool BFS(int Idx)
{
bool Temp_Flag = false;
queue<pair<pair<int, int>, int>> NQ = Q[Idx];
while (NQ.empty() == 0)
{
int x = NQ.front().first.first;
int y = NQ.front().first.second;
int Cnt = NQ.front().second;
NQ.pop();
if (Cnt == Move_Area[Idx]) Q[Idx].pop(); // 원래 존재했던 성이라면, 새롭게 생긴 섬이 아니기 때문에 더이상 관리 X.
for (int i = 0; i < 4; i++)
{
int nx = x + dx[i];
int ny = y + dy[i];
int nCnt = Cnt;
if (nCnt == 0) break;
if (nx >= 0 && ny >= 0 && nx < N && ny < M)
{
if (MAP[nx][ny] == '.')
{
MAP[nx][ny] = Idx + '0';
Count_Area[Idx]++;
Q[Idx].push(make_pair(make_pair(nx, ny), Move_Area[Idx]));
NQ.push(make_pair(make_pair(nx, ny), nCnt - 1));
Temp_Flag = true;
}
}
}
}
return Temp_Flag;
}
void Print()
{
cout << "#################################################" << endl;
for (int i = 0; i < N; i++)
{
for (int j = 0; j < M; j++)
{
cout << MAP[i][j] << " ";
}
cout << endl;
}
cout << "#################################################" << endl;
}
bool Check()
{
for (int i = 1; i <= P; i++)
{
if (Flag[i] == true) return true;
}
return false;
}
void Solution()
{
while (1)
{
for (int i = 1; i <= P; i++)
{
if (Flag[i] == false) continue;
bool Temp_Flag = BFS(i);
if (Temp_Flag == false) Flag[i] = false;
// Print();
}
if (Check() == false) break;
}
for (int i = 1; i <= P; i++)
{
cout << Count_Area[i] << " ";
}
cout << endl;
}
void Solve()
{
Input();
Solution();
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
//freopen("Input.txt", "r", stdin);
Solve();
return 0;
}
'[ BOJ Code ] > # BOJ -' 카테고리의 다른 글
[ 백준 17136 ] 색종이 붙이기 (C++) (12) | 2019.04.07 |
---|---|
[ 백준 17135 ] 캐슬 디펜스 (C++) (4) | 2019.04.07 |
[ 백준 17069 ] 파이프 옮기기2(2) (C++) (0) | 2019.03.25 |
[ 백준 16137 ] 견우와 직녀 (C++) (5) | 2019.03.22 |
[ 백준 10164 ] 격자상의 경로 (C++) (0) | 2019.03.22 |