Posts [백준] 18809번 - Gaaaaaaaaaarden
Post
Cancel

[백준] 18809번 - Gaaaaaaaaaarden


[백준] 18809번 - Gaaaaaaaaaarden

[링크] https://www.acmicpc.net/problem/18809


💎 문제 조건과 설명

문제 조건과 설명은 위의 링크를 참조 부탁드립니다.


🚀 생각한 아이디어

  • 문제에서 주어진 조건을 그대로 작성해서 모든 경우의 수를 확인하면 됩니다.
  • 처음 배양액이 뿌려질 수 있는 땅의 조합 * 그 땅들에 들어갈 배양액 색상 순열 -> BFS로 매 시각마다 퍼뜨리기
  • 처음 배양액이 뿌려질 수 있는 땅(숫자 2로 쓰여진 곳)을 파악해 놓고, 초록색+빨간색 배양액의 수 만큼 조합으로 경우의 수를 구합니다.
    • 예를 들어, 배양액이 뿌려질 수 있는 땅의 갯수가 10군데이고, 총 배양액의 수가 5개라면, 초기에 선택할 수 있는 땅의 경우의 수는 10C5 입니다.
    • next_permutation으로 조합을 구현하는 방식을 택하였습니다.
  • 그렇게 처음 뿌려지는 땅을 골라 놓고, 어떤 색상의 배양액이 올지 정하는 것은, 순열을 이용하여 모든 경우의 수를 대입합니다.
    • 예를 들어 초록색 배양액이 3개, 빨간색 배양액이 2개일 경우, GGGRR, GGRGR, GRGGR, GRRGG, …, RRGGG 로 모든 경우의 수를 대입해 봅니다.
    • 코드상에서는 color 벡터에 색상 값을 넣어놓고, next_permutation을 사용하였습니다.
  • 인접한 칸에 흩뿌리는 것은 큐를 활용한 BFS를 사용하였는데, 매 시각마다 새로 뿌려진 칸을 구분해야 하므로 GREEN_NOW, RED_NOW라는 변수를 별도로 설정하였습니다.
  • _NOW로 표시된 칸에서 다음 흩뿌리기를 진행해야 하므로 이들을 큐에 다시 넣습니다. 단, _NOW로 표시된 칸이라도 과정을 진행하면서 꽃이 피었을 수도 있으므로, 꽃이 아닌 경우에만 큐에 넣으면 됩니다.
  • 혹시 시간 초과가 발생한다면, 땅에 이미 뿌려졌던 배양액들과, 지금 시각에 막 뿌려진 배양액을 구분했는지 확인해 보세요. 그리고 매 시각마다 꽃이 필 수 있는 부분을 정확히 판단하는지 주의해서 보시면 됩니다.

📜 소스코드(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
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#include <iostream>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>

using namespace std;

#define HOSU 0
#define UNABLE 1
#define ABLE 2

#define GREEN 10
#define RED 20
#define FLOWER 1000

#define GREEN_NOW 60
#define RED_NOW 70

int N, M, G, R;
int arr[51][51];
bool visit[51][51];

int dy[4] = { -1,1,0,0 };
int dx[4] = { 0,0,-1,1 };

vector<pair<int, int>> candi;
vector<int> ind;
vector<int> color;

int maxFlower = -987654321;

/* 배양액을 뿌릴 땅을 조합을 이용하여 모든 경우의 수를 본다. */
/* 그 뿌려질 땅에 G 혹은 R이 오는 경우의 수를 순열을 이용하여 모든 경우의 수를 본다. */

void simulation() {

	int copyArr[51][51];
	for (int i = 0; i < N; i++) 
		for (int j = 0; j < M; j++) 
			copyArr[i][j] = arr[i][j];
		
	// G, R 개수에 따른 순열로 각 칸에 뿌리기
	// 배양액을 뿌릴 칸을 조합으로 고르기
    
	do { /* 땅 조합 */
		vector<pair<int, int>> p_land; // 배양액 뿌릴 땅 리스트	
		for (int i = 0; i < ind.size(); i++) {
			if (ind[i] == 1) {
				p_land.push_back(candi[i]);
			}
		}

		do { /* 배양액 색상 순열 */

			int flower = 0;
			queue<pair<int, int>> q;

			// 배양액을 처음에 떨어뜨릴 p_land에, color 순서대로 배양액을 뿌린다.
			for (int i = 0; i < p_land.size(); i++) {
				pair<int, int> p = p_land[i];
				arr[p.first][p.second] = color[i];

				visit[p.first][p.second] = true;
				q.push({ p.first, p.second });
			}

			while (1) {
				vector<pair<int, int>> now; // 지금 뿌려질 초록색 혹은 빨간색 배양액의 위치
				bool flag = false;

				// 사방으로 퍼뜨려 본다.
				while (!q.empty()) {
					int y, x;
					y = q.front().first;
					x = q.front().second;

					q.pop();

					for (int i = 0; i < 4; i++) {
						int ty = y + dy[i];
						int tx = x + dx[i];

						// 호수가 아니고, 꽃이 아닌 곳에 배양액 뿌리기
						if (ty >= 0 && tx >= 0 && ty < N && tx < M &&
							!visit[ty][tx] && arr[ty][tx] != HOSU && arr[ty][tx] != FLOWER) {

							// 다음에 갈 곳이, 방금 뿌려진 배양액이며 다른 색상이었을 경우
							// 그 칸은 꽃으로 만든다.
							if ((arr[y][x] == RED && arr[ty][tx] == GREEN_NOW) ||
								(arr[y][x] == GREEN && arr[ty][tx] == RED_NOW)) {
								arr[ty][tx] = FLOWER;
								flower++;
							}
							else { // 아닐 경우, 그냥 흩뿌린다.
								flag = true;
								arr[ty][tx] = arr[y][x] + 50; // NOW로 뿌린다.
								now.push_back({ ty,tx });
							}
						}
					}
				}

				// 만약 위에서 더 이상 배양액이 퍼뜨려진 적이 없었을 경우 종료
				if (!flag) {
					maxFlower = max(maxFlower, flower); // 값 갱신
					break;
				}

				// now에 있는 칸들을 조사하고 다음을 위해 큐에 넣는다.
				for (int k = 0; k < now.size(); k++) {
					int y = now[k].first;
					int x = now[k].second;
					visit[y][x] = true;

                    // now에 들어간 칸에서 꽃이 피지 않았을 경우에만 다음을 위해 큐로 넣는다.
                    // 꽃이 핀 곳에서는 더 이상 퍼뜨리지 않으므로
					if (arr[y][x] != FLOWER &&
						(arr[y][x] == GREEN_NOW || arr[y][x] == RED_NOW)) {
						arr[y][x] = arr[y][x] - 50;
						q.push({ y,x });
					}
				}
			}

			// 땅 초기화
			for (int i = 0; i < N; i++) 
				for (int j = 0; j < M; j++) {
					arr[i][j] = copyArr[i][j];
					visit[i][j] = false;
				}

		} while (next_permutation(color.begin(), color.end()));
	} while (next_permutation(ind.begin(), ind.end()));
}

int main() {

	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	cin >> N >> M >> G >> R;

	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			cin >> arr[i][j];
			if (arr[i][j] == ABLE) {
				candi.push_back({ i,j });
			}
		}
	}

	// R+G 개수만큼 1을 넣고
	for (int i = 0; i < R + G; i++)
		ind.push_back(1);
	for (int i = 0; i < candi.size() - (R + G); i++)
		ind.push_back(0);
	sort(ind.begin(), ind.end());

	for (int i = 0; i < R; i++)
		color.push_back(RED);
	for (int i = 0; i < G; i++)
		color.push_back(GREEN);
	sort(color.begin(), color.end());

	simulation();
	cout << maxFlower;

	return 0;
}