-
19237.어른상어알고리즘/백준 BAEK JOON 2020. 9. 17. 00:12
이번에 풀어볼 문제 역시 따끈따끈한 최신문제 백준의 19237 어른상어이다.
예전에 아기상어라는 문제를 푼적이 있는데 백준에 어른상어와 청소년상어가 추가되었다.
그 중 어른상어의 문제이다. 난이도는 복잡한 구현만 해결하면 충분히 풀 수 있는 거같다.
시간이 좀 오래걸렸지만 큰 틀(완성직전)까지는 나쁘지않은 시간이 걸린거 같다.다만 예외처리까지 설계하는 힘은 아직 부족한거 같다. 이 문제는 왜인지 모르게 로봇청소기 문제가 떠올랐다.그리고 문제를 완성한 뒤, 디버깅해보니 진짜 상어가 뭔가 살아 움직이는 느낌이 들어서 재밌었다.데이터를 입력받는 부분들은 수월하게 가능했고 상어가 이동하면 냄새의 시간이 줄어드는 부분과상어의 이동들도 굉장히 순탄하게 구현했다. 내가 애를 조금 먹은 부분은 상어의 이동 방향이다.딱, 한가지 조건에서 내 수준에서의 효율적인 코드를 생각하지 못했는데,바로 4방향 탐색 후, 갈 곳이 없다면 자신의 냄새 방향으로 가는 것이다.사실 이 조건이 문제가 아니라 그냥 전체적으로 로직상의 문제라고 생각하는게 더빠른거같다.=> 처음 풀이이다..
해당 문제는 상당히 어려웠다고 생각한다. 그 이유는 구현이 어려운거보다 설계가 어려웠다.
생각해내지 못하면 해결할 수 없는 미궁에 빠진다..
거의 다풀기 직전에서 자꾸 막혀서 여러번 다지우고 다시 시도했는데 항상 같은 부분에서 막혔고
그 부분은 상어의 빈칸 또는 상어를 잡어먹는 부분이었다.
결국 다른 분의 블로그를 참조해서 풀었다. yabmoons.tistory.com/496#comment15909706
해당 내용의 풀이는 다음과 같다.
1. spread 함수
상어의 냄새를 뿌려주고 해당 냄새의 주인을 지정하는 함수 구현
저장된 상어정보의 좌표를 가져와서 냄새의 주인과 냄새를 뿌려준다. ( 좌표를 가져와서 판단하는 것도 중요한 point다 )
중요한 점은 진행된 시간이 기준이 되는 것이다( 이 부분을 나는 생각하지 못했다 )
2. move 함수
상어 이동함수로 map의 공간에 상어를 넣는다.
주의할 점은 해당 공간의 주인을 정하는 함수는 spread함수이지 move 함수가 아니다.
move 함수는 해당 공간에 상어를 넣어주기만하고 중복되지 않는 경우 spread 함수에서 주인이된다.
중복되는 경우 eat함수에서 제거가 된다.
현재 시간에서 냄새의 시간이 현재시간보다 작거나 같은 좌표로 이동한다.
1. 만약 갈 수 있는 곳이 있다면 해당 좌표와 방향을 저장하고 해당 좌표 공간에 상어번호를 저장한다.
갈 수 있는 곳을 탐색하면서 자신의 냄새 좌표를 혹시모르니 미리 저장해둔다.
=> 이 때 중요한 것은 임시좌표를 -1로 초기화 해주어서 해당 -1이 변환되면
이미 자기냄새를 찾은 것이므로 pass하는 로직이 필요하다. ( 86, 106 라인 참고 )
=> 이걸 하지 않으면 자신의 냄새가 주변에 여러개 있을 때 우선순위가 느린 좌표가 저장된다..
2. 갈수 있는 곳이 없다면 자신의 냄새좌표로 이동한다.
3. eat 함수
한 공간에 여러 상어가 존재할 시, 번호가 작은 상어가 나머지 상어를 모두 잡아먹는다.
이 경우 제일 번호가 작은 상어를 제외하고 모두 제거한다. 그리고
해당 내용이 끝나면 다시 spread 함수가 진행되면서 해당 공간의 냄새의 주인이 결정되므로
공간에 저장된 모든 상어를 제거해줘야한다. -> 다음 move 함수의 탐색을 위해서..
move에서 빈칸을 찾아가는 기준이 map에 저장된 상어 냄새의 주인이나 빈칸의 여부가 아닌 갈 수있는지 판단하는 시간이므로
< Code 설명 >
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
#pragma warning(disable:4996)#include <iostream>#include <vector>#include <algorithm>#define MAX 21#define MAX2 401using namespace std;int dx[5] = { 0, -1, 1, 0, 0 };int dy[5] = { 0, 0, 0, -1, 1 };struct shark{int x, y, dir; // 좌표 및 방향 변수bool life; // 생존여부 변수vector<int>p[5]; // 우선순위 vector};struct info{ // map 정보int smell, shark; // 몇초뒤에 갈 수있는지에 대한 냄새 변수, 해당 냄새의 주인vector<int>s; // 해당 공간에 존재하는 상어 저장 vector};int N, M, K;info map[MAX][MAX]; // map 정보에 대한 배열shark sk[MAX2]; // 상어 정보에 대한 배열int die, ans; // 죽은 상어 카운트 변수, 정답 변수void showme(){ // 디버깅 함수for (int i = 0; i < N; i++){for (int j = 0; j < N; j++){cout << map[i][j].shark << " ";}cout << endl;}cout << endl;for (int i = 0; i < N; i++){for (int j = 0; j < N; j++){cout << map[i][j].smell << " ";}cout << endl;}cout << endl;}void eat(){ // 상어를 잡아먹는 함수for (int i = 1; i <= M; i++){if (!sk[i].life) // 죽은 상어는 탐색 제외continue;int x = sk[i].x;int y = sk[i].y;if (map[x][y].s.size() >= 2){ // 2개이상이라는 의미 => 같은 칸에 여러 상어가 존재하는 경우sort(map[x][y].s.begin(), map[x][y].s.end()); // 오름차순으로 정렬//map[x][y].shark = map[x][y].s.front();// 제일 앞에 있는 상어의 번호가 가장 작기떄문에 해당 내용이 해당 칸을 차지한다. 굳이 필요는 없음 이유는 먹히고나면 탐색에서 제외되기 때문이다.for (int j = 1; j < map[x][y].s.size(); j++){ // 제일 앞을 제외한 나머지 중복 상어들 확인sk[map[x][y].s[j]].life = false; // 나머지 상어들은 잡아먹음die++; // 잡아먹힌 상어수 +1//cout << "[-] "<< map[x][y].s[j] << " shark is dead " << endl;}map[x][y].s.clear(); // 해당 좌표의 주인상어가 정해졌으므로 해당 겹치는 구간 제거}}for (int i = 1; i <= M; i++){ // 상어가 중복 있던 좌표들은 이미 주인이 정해졌으므로if (!sk[i].life) // 죽은 상어는 탐색 제외continue;int x = sk[i].x;int y = sk[i].y;map[x][y].s.clear(); // 잡아먹어서 죽은 상어 외에도 나머지 상어들도 다음 탐색을 위해 중복 칸에 대한 내용을 모두 비움}}void move(int time){for (int i = 1; i <= M; i++){ // 그리고 다시 이동 하면서 중복칸에 대한 내용을 채워감if (!sk[i].life) // 죽은 상어는 탐색 제외continue;int x = sk[i].x; // 해당 상어의 좌표 변수int y = sk[i].y; // 해당 상어의 좌표 변수int dir = sk[i].dir; // 해당 상어의 방향 변수bool flag = false; // 빈칸을 발견했는지에 대한 유무 변수int tmp_x, tmp_y, tmp_dir; // 4방향 탐색 중에 자신과 같은 냄새 발견시에 미리 저장해둠tmp_x = tmp_y = tmp_dir = -1; // -1로 초기화 -> 임시 저장한적이 있는지 없는지 판단을 위해// 이유는 이걸 구별해주지 않으면 나와 같은 냄새가 여러곳에 있을 때 이미 우선순위로 찾았는데도// 불구하고 제일 우선순위가 느린 녀석이 덮어쓰임for (int j = 0; j < 4; j++){ // 4방향 탐색 ( 해당 상어의 우선순위 방향으로 4방향 탐색 )int ax = x + dx[sk[i].p[dir][j]]; // 우선순위 기준 4방향int ay = y + dy[sk[i].p[dir][j]]; // 우선순위 기준 4방향if (ax < 0 || ay < 0 || ax >= N || ay >= N) // 범위초과시 탐색 passcontinue;if (map[ax][ay].smell <= time){ // 해당 smell 변수는 해당 시간 이후부터 갈 수 있는 공간(빈공간이되므로)이므로flag = true; // 빈칸 발견했으므로 flag truesk[i].x = ax; // 냄새가 사라져서 갈 수 있는 칸인 경우 해당 좌표 저장sk[i].y = ay; // 냄새가 사라져서 갈 수 있는 칸인 경우 해당 좌표 저장sk[i].dir = sk[i].p[dir][j]; // 빈칸을 찾은 방향 저장map[ax][ay].s.push_back(i); // 해당 공간에 상어 번호 저장 -> 해당좌표의 냄새의 주인은 해당공간에 중복이 없을 시, spread 함수에서 결정됨break;}else if (map[ax][ay].shark == i){ // 자신과 같은 냄새 발견시 혹시 모르니 좌표 저장if (tmp_x == -1){// 자신과 같은 냄새를 발견했는데 아직 임시저장한 적이 없다면 => 임시저장한 적이 있다면 이미 우선순위중 가장 빠른 자기 냄새 정보가 저장됨tmp_x = ax; // 자신의 냄새 좌표 미리 저장tmp_y = ay; // 자신의 냄새 좌표 미리 저장tmp_dir = sk[i].p[dir][j]; // 자신의 냄새 방향 미리 저장}}}if (!flag){ // 빈칸을 발견하지 못했을 때(4방향 탐색에도 갈 곳이 없을때)map[tmp_x][tmp_y].s.push_back(i); // 해당 공간에 상어번호 저장sk[i].x = tmp_x; // 미리 저장해놓은 자신의 냄새쪽 좌표로 이동sk[i].y = tmp_y; // 미리 저장해놓은 자신의 냄새쪽 좌표로 이동sk[i].dir = tmp_dir; // 미리저장해놓은 자신의 방향으로 정보 변경}}}void spread(int time){ // 냄새 뿌리는 함수for (int i = 1; i <= M; i++){if (!sk[i].life) // 죽은 상어 탐색 제외continue;int x = sk[i].x;int y = sk[i].y;map[x][y].shark = i; // 해당 냄새의 주인 저장map[x][y].smell = time + K; // 해당 냄새가 몇초 후에 사라질 것인지 저장}}void search(){// 큰 틀에서의 함수int time = 0; // 시간을 카운트하기위한 변수while (time <=1000){ // 1000을 넘지 않는 선에서 시간 카운트if (die == M-1){ // 상어가 1마리남으면ans = time; // 해당 시간 저장 후 종료break;}spread(time); // 냄새를 뿌리고 해당 칸의 냄새주인을 저장하는 함수//showme(); // 디버깅move(time); // 상어 움직이는 함수eat(); // 겹치는 상어 잡아먹는 함수time+=1; // 시간 카운트 +1}if (time == 1001) // 1000보다 크면ans = -1; // -1 출력을 위해 저장}int main(){freopen("19237.txt","r",stdin);cin >> N >> M >> K;int shark;for (int i = 0; i < N; i++){for (int j = 0; j < N; j++){cin >> shark;if (shark != 0){sk[shark] = { i, j, 0, true };}}}int dir;for (int i = 1; i <= M; i++){cin >> dir;sk[i].dir = dir;}int tmp;for (int i = 1; i <= M; i++){for (int j = 1; j <= 4; j++){for (int z = 0; z < 4; z++){cin >> tmp;sk[i].p[j].push_back(tmp);}}}search();cout << ans << endl;return 0;}cs
< Code 설명 2020.10.17 >
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
#pragma warning(disable:4996)#include <iostream>#include <string.h>#include <vector>#define MAX 21using namespace std;int N, M, K;int dx[5] = {0,-1,1,0,0};int dy[5] = {0,0,0,-1,1};struct fish{int x, y, dir;bool life;vector<int>pri[5];};struct info{int master;int time;vector<int>temp;};info map[MAX][MAX];fish shark[MAX * MAX];int shark_cnt;void showme(){cout << "shark-------------------------" << endl;for (int i = 1; i <= N; i++){for (int j = 1; j <= N; j++){cout << map[i][j].master << " ";}cout << "\n";}cout << "\n";cout << "smell time-------------------------" << endl;for (int i = 1; i <= N; i++){for (int j = 1; j <= N; j++){cout << map[i][j].time << " ";}cout << "\n";}cout << "\n";}void move(){int cnt = 0;for (int i = 1; i <= M; i++){if (shark[i].life){int x = shark[i].x;int y = shark[i].y;int dir = shark[i].dir;for (int j = 0; j < 4; j++){int ax = x + dx[shark[i].pri[dir][j]];int ay = y + dy[shark[i].pri[dir][j]];if (ax <= 0 || ay <= 0 || ax > N || ay > N){cnt++;continue;}if (map[ax][ay].master == 0){map[ax][ay].temp.push_back(i);shark[i].x = ax;shark[i].y = ay;shark[i].dir = shark[i].pri[dir][j];cnt = 0;break;}cnt++;}if (cnt >= 4){for (int j = 0; j < 4; j++){int ax = x + dx[shark[i].pri[dir][j]];int ay = y + dy[shark[i].pri[dir][j]];if (ax <= 0 || ay <= 0 || ax > N || ay > N)continue;if (map[ax][ay].master == i){map[ax][ay].temp.push_back(i);shark[i].x = ax;shark[i].y = ay;shark[i].dir = shark[i].pri[dir][j];break;}}}}}}void spread(){for (int i = 1; i <= M; i++){if (shark[i].life){int x = shark[i].x;int y = shark[i].y;map[x][y].time = K;}}}void eat(){for (int i = 1; i <= M; i++){if (shark[i].life){int x = shark[i].x;int y = shark[i].y;int ans = 987654321;for (int t = 0; t < map[x][y].temp.size(); t++){if (map[x][y].temp[t] < ans)ans = map[x][y].temp[t];}for (int t = 0; t < map[x][y].temp.size(); t++){if (map[x][y].temp[t] != ans){shark[map[x][y].temp[t]].life = false;shark_cnt--;}}map[x][y].master = ans;map[x][y].time = K;map[x][y].temp.clear();}}}void time_is_gone(){for (int i = 1; i <= N; i++){for (int j = 1; j <= N; j++){if (map[i][j].time == 0)map[i][j].master = 0;if (map[i][j].time >= 1){map[i][j].time -= 1;}}}}void solve(){int flag = false;for (int t = 0; t <= 1000; t++){if (shark_cnt == 1){cout << t << endl;flag = true;break;}spread();time_is_gone();move();eat();//showme();}if (!flag)cout << -1 << endl;}int main() {freopen("19237.txt", "r", stdin);cin >> N >> M >> K;int num;shark_cnt = M;for (int i = 1; i <= N; i++){for (int j = 1; j <= N; j++){cin >> num;if (num >= 1){map[i][j].master = num;shark[num] = {i,j,0,true};}}}int tmp;for (int i = 1; i <= M; i++){cin >> tmp;shark[i].dir = tmp;}int a, b, c, d;for (int i = 1; i <= M; i++){for (int j = 1; j <= 4; j++){cin >> a >> b >> c >> d;shark[i].pri[j].push_back(a);shark[i].pri[j].push_back(b);shark[i].pri[j].push_back(c);shark[i].pri[j].push_back(d);}}solve();return 0;}cs 반응형'알고리즘 > 백준 BAEK JOON' 카테고리의 다른 글
19236.청소년상어 (0) 2020.09.09 19238.스타트 택시 (0) 2020.08.30 1197.최소 스패닝 트리 (0) 2020.08.20 9372.상근이의 여행 (0) 2020.08.20 1932.정수 삼각형 (0) 2020.08.19 댓글