From a67799b562a9864a9ec9e845d1298b521cb97a5c Mon Sep 17 00:00:00 2001 From: HyoJin Joo Date: Tue, 22 Apr 2025 22:05:46 +0900 Subject: [PATCH] feat: Puyo Puyo --- 01_PuyoPuyo/hyoz/code.py | 67 ++++++++++++++ 01_PuyoPuyo/hyoz/description.ipynb | 142 +++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 01_PuyoPuyo/hyoz/code.py create mode 100644 01_PuyoPuyo/hyoz/description.ipynb diff --git a/01_PuyoPuyo/hyoz/code.py b/01_PuyoPuyo/hyoz/code.py new file mode 100644 index 0000000..c66a6a3 --- /dev/null +++ b/01_PuyoPuyo/hyoz/code.py @@ -0,0 +1,67 @@ +from collections import deque +field = [] +directions = [[-1, 0], [0, -1], [1, 0], [0, 1]] # 상하좌우 + + +def bfs(color, x, y, visited): + ''' + 동일 색상에 대해 BFS를 수행하는 함수 (반환값: 인접한 동일한 색상의 개수) + + + - color: 현재 탐색해야 할 색상 + - x, y: 시작 좌표 + - visited: 방문 여부 판단 배열 + ''' + queue = deque([(x, y)]) # BFS 탐색용 큐 + coords = [(x, y)] # 4개 이상일 때 제거를 위해 반환할 좌표 배열 + cnt = 1 + while queue: + cx, cy = queue.popleft() + + visited[cx][cy] = True # 방문 처리 + for dx, dy in directions: + nx, ny = cx+dx, cy+dy + if (nx >= 0 and nx < 6) and (ny >= 0 and ny < 12): # 배열 범위 내 + if field[nx][ny] == color and not visited[nx][ny]: + queue.append((nx, ny)) + coords.append((nx, ny)) + cnt += 1 + visited[nx][ny]=True + return cnt, coords + + +# 필드 입력 +for _ in range(12): + field.append(input()) + +# 필드 90도 시계방향으로 회전 +field = [list(e) for e in zip(*field[::-1])] + +answer = 0 +while True: + visited = [[False]*12 for _ in range(6)] # 방문 여부 판단 배열 + + remove_coords = [] # 1연쇄 시 제거해야 할 좌표들 + + for x in range(6): + for y in range(12): + if field[x][y] != '.': + if not visited[x][y]: + cnt, coords = bfs(field[x][y], x, y, visited) + if cnt >= 4: + remove_coords.extend(coords) + + if len(remove_coords) > 0: + # Clear + Xs = [] + for x in range(6): + col = '' + for y in range(12): + if (x, y) not in remove_coords and field[x][y] != '.': + col += field[x][y] + field[x] = list(col.ljust(12, '.')) + + answer += 1 + else: + print(answer) + break \ No newline at end of file diff --git a/01_PuyoPuyo/hyoz/description.ipynb b/01_PuyoPuyo/hyoz/description.ipynb new file mode 100644 index 0000000..3ea498f --- /dev/null +++ b/01_PuyoPuyo/hyoz/description.ipynb @@ -0,0 +1,142 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0996ea12-48c5-4477-9021-61bc706bca9f", + "metadata": {}, + "source": [ + "# Description" + ] + }, + { + "cell_type": "markdown", + "id": "46642a35-6e36-49bb-ab6b-bcab05a2e8e4", + "metadata": {}, + "source": [ + "### 문제 개요\n", + "* 문제: [Puyo Puyo](https://www.acmicpc.net/problem/11559)\n", + "* 문제 요약\n", + " * 동일한 색상이 4개 이상 인접한 블럭을 제거한다. 제거된 블럭 위에 있던 블럭은 아래로 떨어지며, 떨어진 후 4개 이상 인접한 블럭이 있을 경우 또 다시 제거하는 연쇄 작용이 발생한다. 이러한 연쇄 작용이 몇 번 일어나는지 찾는 문제이다.\n", + " * 입력: 5가지 색상(R, G, B, P, Y)의 블럭과 빈칸(.)으로 구성된 2차원 배열\n", + " * 출력: 연쇄 작용이 일어난 횟수 (정수값)\n", + "* 문제 유형: 그래프 탐색\n" + ] + }, + { + "cell_type": "markdown", + "id": "efa2d101-5e24-4ae2-98f2-973ca0aa9fed", + "metadata": {}, + "source": [ + "# History of Code" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6293dd8-87a6-4f72-aa3e-98580143c5e0", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "시간 복잡도: O(n^3)\n", + "\n", + "구현 순서\n", + "1. 입력 받은 2차원 좌표를 돌면서 방문하지 않은 색상 블럭에 대해 BFS 함수를 수행한다. -> O(n^2)\n", + "1-1. 이 때, 입력받은 2차원 배열은 시계 방향으로 90도 회전하여 아래로 내려오는 한 라인을 하나의 배열에 할당한다.\n", + "2. BFS를 통해 인접한 동일 색상의 블럭 개수를 반환하고, 이 개수가 4개 이상인 경우 제거 블록에 추가한다.\n", + "3. 제거 블록이 있는 경우, 해당 좌표를 제거한 후 남은 부분을 빈칸으로 채워주고 1번으로 다시 돌아간다.\n", + "4. 제거 블럭이 없는 경우 현재 연쇄 횟수를 출력하고 반환한다.\n", + "\"\"\"\n", + "\n", + "from collections import deque\n", + "field = []\n", + "directions = [[-1, 0], [0, -1], [1, 0], [0, 1]] # 상하좌우\n", + "\n", + "\n", + "def bfs(color, x, y, visited):\n", + " ''' \n", + " 동일 색상에 대해 BFS를 수행하는 함수 (반환값: 인접한 동일한 색상의 개수)\n", + " \n", + " \n", + " - color: 현재 탐색해야 할 색상\n", + " - x, y: 시작 좌표\n", + " - visited: 방문 여부 판단 배열\n", + " '''\n", + " queue = deque([(x, y)]) # BFS 탐색용 큐\n", + " coords = [(x, y)] # 4개 이상일 때 제거를 위해 반환할 좌표 배열\n", + " cnt = 1\n", + " while queue:\n", + " cx, cy = queue.popleft()\n", + " \n", + " visited[cx][cy] = True # 방문 처리\n", + " for dx, dy in directions:\n", + " nx, ny = cx+dx, cy+dy\n", + " if (nx >= 0 and nx < 6) and (ny >= 0 and ny < 12): # 배열 범위 내\n", + " if field[nx][ny] == color and not visited[nx][ny]:\n", + " queue.append((nx, ny))\n", + " coords.append((nx, ny))\n", + " cnt += 1\n", + " visited[nx][ny]=True\n", + " return cnt, coords\n", + "\n", + "\n", + "# 필드 입력\n", + "for _ in range(12):\n", + " field.append(input())\n", + "\n", + "# 필드 90도 시계방향으로 회전\n", + "field = [list(e) for e in zip(*field[::-1])]\n", + "\n", + "answer = 0\n", + "while True:\n", + " visited = [[False]*12 for _ in range(6)] # 방문 여부 판단 배열\n", + "\n", + " remove_coords = [] # 1연쇄 시 제거해야 할 좌표들\n", + "\n", + " for x in range(6):\n", + " for y in range(12):\n", + " if field[x][y] != '.':\n", + " if not visited[x][y]:\n", + " cnt, coords = bfs(field[x][y], x, y, visited)\n", + " if cnt >= 4: \n", + " remove_coords.extend(coords)\n", + "\n", + " if len(remove_coords) > 0:\n", + " # Clear\n", + " Xs = []\n", + " for x in range(6):\n", + " col = ''\n", + " for y in range(12):\n", + " if (x, y) not in remove_coords and field[x][y] != '.':\n", + " col += field[x][y]\n", + " field[x] = list(col.ljust(12, '.'))\n", + "\n", + " answer += 1\n", + " else:\n", + " print(answer)\n", + " break" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}