728x90
반응형

불 대수 : AND, OR, NOT

NOT 연산은 그냥 반대로 뒤집어 주면 된다.

 

불린형 (Boolean) : True, False

무조건 따옴표 없이 써야된다. 불린형으로 불대수 계산이 가능하다.

print(True and True)#True
print(True and False)#False
print(False and True)#False
print(False and False)#False
print(True or True)#True
print(True or False)#True
print(False or True)#True
print(False or False)#False
print(2>1)#True
print(3<=3)#True

 

type 함수

이제까지 배운 자료형을 알아볼 수 있는 함수이다.

print(type(3))#<class 'int'>
print(type(3.0))#<class 'float'>
print(type("3"))#<class 'str'>
728x90
반응형
728x90
반응형

https://www.acmicpc.net/problem/1261

 

1261번: 알고스팟

첫째 줄에 미로의 크기를 나타내는 가로 크기 M, 세로 크기 N (1 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 미로의 상태를 나타내는 숫자 0과 1이 주어진다. 0은 빈 방을 의미하고, 1은 벽을 의미

www.acmicpc.net

문제

알고스팟 운영진이 모두 미로에 갇혔다. 미로는 N*M 크기이며, 총 1*1크기의 방으로 이루어져 있다. 미로는 빈 방 또는 벽으로 이루어져 있고, 빈 방은 자유롭게 다닐 수 있지만, 벽은 부수지 않으면 이동할 수 없다.

알고스팟 운영진은 여러명이지만, 항상 모두 같은 방에 있어야 한다. 즉, 여러 명이 다른 방에 있을 수는 없다. 어떤 방에서 이동할 수 있는 방은 상하좌우로 인접한 빈 방이다. 즉, 현재 운영진이 (x, y)에 있을 때, 이동할 수 있는 방은 (x+1, y), (x, y+1), (x-1, y), (x, y-1) 이다. 단, 미로의 밖으로 이동 할 수는 없다.

벽은 평소에는 이동할 수 없지만, 알고스팟의 무기 AOJ를 이용해 벽을 부수어 버릴 수 있다. 벽을 부수면, 빈 방과 동일한 방으로 변한다.

만약 이 문제가 알고스팟에 있다면, 운영진들은 궁극의 무기 sudo를 이용해 벽을 한 번에 다 없애버릴 수 있지만, 안타깝게도 이 문제는 Baekjoon Online Judge에 수록되어 있기 때문에, sudo를 사용할 수 없다.

현재 (1, 1)에 있는 알고스팟 운영진이 (N, M)으로 이동하려면 벽을 최소 몇 개 부수어야 하는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 미로의 크기를 나타내는 가로 크기 M, 세로 크기 N (1 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 미로의 상태를 나타내는 숫자 0과 1이 주어진다. 0은 빈 방을 의미하고, 1은 벽을 의미한다.

(1, 1)과 (N, M)은 항상 뚫려있다.

출력

첫째 줄에 알고스팟 운영진이 (N, M)으로 이동하기 위해 벽을 최소 몇 개 부수어야 하는지 출력한다.

 

예제 입력 1 

3 3

011

111

110

예제 출력 1 

3

예제 입력 2 

4 2

0001

1000

예제 출력 2 

0

예제 입력 3 

6 6

001111

010000

001111

110001

011010

100010

예제 출력 3 

2

 

[문제 해설]

이 문제는 벽에 대한 것을 어떻게 해결해야될까가 고민이였다. 벽을 부수는 횟수를 최소한으로 해서 탈출을 해야 한다. 사실 벽을 부순다는 행위를 거리의 길이 즉 가중치라고 생각하면 된다. 그래서 거리가 짧은 곳으로 다녀 목적지까지 도달하게 해주는 다익스트라 알고리즘을 사용하면 된다. 다익스트라 알고리즘보다는 거의 BFS를 사용해서 푼 것 같다.

 

BFS대로 푸는데 최대한 벽이 없는 곳으로 가는 것이 유리하다. 벽이 없는 곳을 만났을 때는 큐에서 appendleft를 이용하여 먼저 가게 하고 벽이 있는 곳을 만나면 그냥 append를 통해 큐에 추가해준다. 

 

from collections import deque
m,n=map(int,input().split())
graph=[list(map(int,input())) for _ in range(n)]
bback=[[-1]*m for _ in range(n)]#가중치
dx=[-1,1,0,0]#상하좌우
dy=[0,0,-1,1]#상하좌우

q=deque()
q.append((0,0))
bback[0][0]=0
while q:
    x,y = q.popleft()
    for i in range(4):
        nx=x+dx[i]
        ny=y+dy[i]
        if nx<0 or nx>=n or ny<0 or ny>=m:
            continue
        if bback[nx][ny]==-1:
            if graph[nx][ny]==0:
                bback[nx][ny]=bback[x][y]
                q.appendleft((nx,ny))
            elif graph[nx][ny]==1:
                bback[nx][ny]=bback[x][y]+1
                q.append((nx,ny))
print(bback[n-1][m-1])

 

 

728x90
반응형
728x90
반응형

최단 경로 Shortest Path 알고리즘은 말 그대로 가장 짧은 경로를 찾는 알고리즘이다. 

최단 경로 알고리즘 유형에는 여러가지가 있다.

'한 지점에서 다른 특정 지점까지의 최단 경로를 구해야 하는 경우', '모든 지점에서 다른 모든 지점까지의 최단 경로를 모두 구해야 하는 경우' 등등.

최단 경로 알고리즘은 보통 그래프로 표현한다. 각 지점은 그래프에서 노드로 표현되고, 지점 간 연결된 도로는 그래프에서 간선으로 표현된다. 

 

여기서는 다익스트라(Dijkstra's algorithm)에 대해 배우겠다.

그리디 알고리즘과 다이나믹 알고리즘이 최단 경로 알고리즘에 그대로 적용된다.

 

다익스트라 알고리즘은, 그래프 내의 특정 정점에서 갈 수 있는 모든 정점들까지의 최단 경로를 구하는 알고리즘이다.

특정한 노드에서 출발, 다른 노드로 가는 각각의 최단 경로를 구해주는 알고리즘인 것이다. 

 

그래프가 큰 경우에도 사용할 수 있어 효율적이지만,

'음의 간선'이 있으면 정상적으로 작동하지 않는다. '음의 간선'이란 0보다 작은 값을 가지는 간선을 의미한다.

 

다익스트라 알고리즘은 노드 주변을 탐색할 때 BFS(너비 우선 탐색)를 이용하는 대표적인 알고리즘이기도 하다.

//DFS와 BFS 관련

jobdong7757.tistory.com/64

 

DFS / BFS

탐색Search이란 많은 양의 데이터 중에서 원하는 데이터를 찾는 과정을 의미한다. 대표적인 탐색 알고리즘이 DFS와 BFS이다. 프로그래밍에서는 자료구조 안에서 탐색을 하는데 DFS와 BFS를 알려면 스

jobdong7757.tistory.com

DFS는 한 사람이 미로를 찾아 헤매는 과정과 비슷한 반면, BFS는 여러 명의 사람이 각기 서로 다른 갈림길로 흩어져서 길을 찾는 것과 비슷하다. 이때 각자 실뭉치를 가지고 풀어 놓았다가 되감으면서 새로운 길을 탐색한다. 길을 따라가다 갈림길이 나오면 사람들을 나눠 다시 각자 새로운 길을 탐색하고, 다시 갈림길을 만나 새로 탐색할 길이 하나로 모이면, 나뉘어 탐색하던 사람들이 다시 모여서 함께 탐색한다. 

다익스트라 알고리즘은 이때 먼저 도착한 사람의 실뭉치를 사용한다. 

 

다익스트라 알고리즘은 임의의 정점을 출발 집합에 더할 때, 그 정점까지의 최단거리는 계산이 끝났다는 확신을 가지고 더한다. 만일 이후에 더 짧은 경로가 존재한다면 다익스트라 알고리즘의 논리적 기반이 무너진다. 이때는 모두 값을 더해서 양수로 변환하는 방법이 있다. 같은 이유로 최장거리를 구하는 데에는 다익스트라 알고리즘을 사용할 수 없다.

 

다익스트라 알고리즘은 매번 가장 비용이 적은 노드를 선택해서 임의의 과정으로 반복하기 때문에 그리디 알고리즘으로 분류된다 알고리즘의 과정은 다음과 같다.

 

1. 출발 노드를 설정한다.

2. 최단 거리 테이블을 초기화 한다. (초기에는 전부 무한대 값으로 초기화 한다)

3. 방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택한다.

4. 해당 노드를 거쳐 다른 노드로 가는 비용을 계산하여 최단 거리 테이블을 갱신한다.

5. 위 과정의 3,4를 반복한다.

 

다익스트라 알고리즘은 최단 경로를 구하는 과정에서 '각 노드에 대한 현재까지의 최단 거리' 정보를 항상 1차원 리스트에 저장하며 리스트를 계속 갱신한다. 3번에서 방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택해서 4번을 수행한다는 점에서 그리디 알고리즘으로 볼 수 있다.

다익스트라 최단 경로 알고리즘에서는 '방문하지 않은 노드 중에서 가장 최단 거리가 짧은 노드를 선택'하는 과정을 반복한다. 그래서 최적의 해답을 구할 수 있다.

 

다익스트라 알고리즘 구현

1. 첫번째 방법

import sys
input = sys.stdin.readline
INF = int(1e9)#무한

#노드의 개수, 간선의 개수 입력 받기
n,m = map(int,input().split())
#시작 노드
start = int(input())
#각 노드에 연결되어 있는 노드에 대한 정보 담는 리스트 만들기
graph=[[] for i in range(n+1)]
#방문 리스트
visited = [False]*(n+1)
#최단 거리 테이블 무한으로 초기화
distance = [INF]*(n+1)
#모든 간선 정보 입력받기
for _ in range(m):
    a,b,c = map(int,input().split())
    #a번 노드에서 b번 노드로 가는 비용이 c
    graph[a].append((b,c))

#방문하지 않은 노드 중에서, 가장 최단 거리가 짧은 노드의 번호를 반환
def get_smallest_node():
    min_value = INF
    index =0 #가장 최단 거리가 짧은 노드
    for i in range(1,n+1):
        if distance[i]<min_value and not visited[i]:
            min_value = distance[i]
            index = i
    return index

def dijkstra(start):
    #시작 노드 초기화
    distance[start]=0
    visited[start]=True
    for j in graph[start]:
        distance[j[0]]=j[1]
    #시작 노드를 제외한 전체 n-1개의 노드에 대해 반복
    for i in range(n-1):
        #현재 최단 거리가 가장 짧은 노드를 꺼내서, 방문 처리
        now = get_smallest_node()
        visited[now]=True
        #현재 노드와 연결된 다른 노드를 확인
        for j in graph[now]:
            cost = distance[now]+j[1]
            #현재 노드를 거쳐서 다른 노드로 이동하는 거리가 더 짧은 경우
            if cost<distance[j[0]]:
                distance[j[0]]=cost
#다익스트라 알고리즘 실행
dijkstra(start)

#모든 노드로 가기 위한 최단 거리를 출력
for i in range(1,n+1):
    #도달할 수 없는 경우, 무한(INFINITY)출력
    if distance[i]==INF:
        print("INFINITY")
    #도달할 수 있는 경우 거리를 출력
    else:
        print(distance[i])

-첫번째 다익스트라 알고리즘의 시간 복잡도 : O(N^2)

다익스트라 알고리즘의 코드를 보면 전체 노드에서 for문을 돌릴때 get_smallest_node()함수를 계속 사용한다. 

get_smallest_node()함수에서는 전체 노드에서 제일 거리가 짧은 노드를 찾기 때문에 O(V)번 걸린다. 결국 시간복잡도는 O(V^2)이 된다. 따라서 노드의 개수가 10000개를 넘어가면 문제를 해결할 때 이 방법을 쓰면 시간 초과가 날 수 있다.

 

2. 두번째 방법

-두번째 다익스트라 알고리즘의 시간 복잡도 : O(MlogN)

이 방법은 최악의 경우에도 O(MlogN)을 보장하여 해결할 수 있다. 여기서 N은 노드의 개수, M은 간선의 개수에 해당한다. 첫번째 알고리즘에서 최단 거리 노드를 하나하나씩 탐색했다면 이 과정에서 시간을 단축하여 알고리즘을 개선하였다. 여기서는 힙 자료구조를 사용한다. 힙 자료구조를 이용하게 되면 특정 노드까지의 최단 거리에 대한 정보를 힙에 담아서 처리하므로 출발 노드로부터 가장 거리가 짧은 노드를 더욱 빠르게 찾을 수 있다. 이 과정에서 로그 시간이 걸린다.

 

//힙 관련

jobdong7757.tistory.com/69

 

힙 자료구조

힙과 우선순위 큐 자료구조 힙(heap): 힙 자료구조는 완전 이진 트리의 일종으로 우선순위 큐(Prioirty Queue)를 위하여 만들어진 자료구조이다. 힙을 사용한 모듈은 heapq 모듈이다. 파이썬은 최소 힙

jobdong7757.tistory.com

데이터의 개수가 N개일 때, 힙 자료구조에 N개의 데이터를 모두 넣은 뒤에 다시 모든 데이터를 꺼낸다고 해보자. 이때의 시간 복잡도는 어떻게 될가? 삽입할 때는 O(logN)의 연산을 N번 반복하므로 O(NlongN)이고 삭제할 때에서 O(logN)의 연산을 N번 반복하므로 O(NlogN)이다. 따라서 전체 연산 횟수는 대략 2NlogN으로 시간복잡도는 O(NlogN)이 될 것이다. 

 

최소 힙을 이용하는 경우 힙에서 원소를 꺼내면 '가장 값이 작은 원소'가 추출되는 특징이 있으며, 파이썬의 우선순위 큐라이브러리는 최소 힙에 기반한다. 우선순위 큐를 이용해서 시작 노드로부터 '거리'가 짧은 노드 순서대로 큐에서 나올 수 잇도록 다익스트라 알고리즘을 작성하면 된다. 

 

1번 방법에서 현재 가장 가까운 노드를 저장하기 위한 목적으로만 우선순위 큐를 추가로 이용한다고 보면 된다.  파이썬의 heapq 라이브러리는 원소로 튜플을 입력받으면 튜플의 첫 번째 원소를 기준으로 우선순위 큐를 구성한다. 따라서 (거리, 노드 번호) 순서대로 튜플 데이터를 구성해 우선순위 큐에 넣으면 거리순으로 정렬된다.

 

import heapq
import sys
input = sys.stdin.readline
INF = int(1e9)
#노드의 개수, 간선의 개수 입력받기
n,m = map(int,input().split())
#시작 노드 번호를 입력받기
start = int(input())
#각 노드에 연결되어 있는 노드에 대한 정보를 담는 리스트 만들기
graph=[[] for i in range(n+1)]
#최단 거리 테이블을 모두 무한으로 초기화
distance=[INF]*(n+1)

#모든 간선 정보를 입력받기
for _ in range(m):
    a,b,c = map(int,input().split())
    #a번 노드에서 b번 노드로 가는 비용이 c
    graph[a].append((b,c))

def dijkstra(start):
    q=[]
    #시작 노드로 가기 위한 최단 경로는 0으로 설정하여, 큐에 삽입
    heapq.heappush(q,(0,start))
    distance[start]=0
    while q:#큐가 비어있지 않다면
        #가장 최단 거리가 짧은 노드에 대한 정보 꺼내기
        dist,now = heapq.heappop(q)
        #현재 노드가 이미 처리된 적이 있는 노드라면 무시
        if distance[now]<dist:
            continue
        #현재 노드와 연결된 다른 인접한 노드들을 확인
        for i in graph[now]:
            cost = dist + i[1]
            #현재 노드를 거쳐서, 다른 노드로 이동하는 거리가 더 짧은 경우
            if cost < distance[i[0]]:
                distance[i[0]] = cost
                heapq.heappush(q,(cost,i[0]))
#다익스트라 알고리즘 수행
dijkstra(start)

#모든 노드로 가기 위한 최단 거리 출력
for i in range(1,n+1):
    if distance[i]==INF:
        print("INFINITY")
    else:
        print(distance[i])
728x90
반응형

'알고리즘 > 알고리즘 이론' 카테고리의 다른 글

힙 자료구조  (0) 2021.05.04
DFS / BFS  (0) 2021.04.12
정렬(Sort)  (0) 2021.04.12
재귀 함수  (0) 2021.04.02
브루트 포스 (Brute Force)  (0) 2021.03.18
728x90
반응형

힙과 우선순위 큐

자료구조 힙(heap):

힙 자료구조는 완전 이진 트리의 일종으로 우선순위 큐(Prioirty Queue)를 위하여 만들어진 자료구조이다. 힙을 사용한 모듈은 heapq 모듈이다. 파이썬은 최소 힙만 구현되어 있다. 최소 힙은 부모가 항상 자식보다 작기 때문에 루트가 결국 가장 작은 값을 갖게 되며, 우선순위 큐에서 가장 작은 값을 추출하는 것은 매번 힙의 루트를 가져오는 형태로 구현된다. 

우선순위 큐(Prioirty Queue):

우선순위 큐는 우선순위가 가장 높은 데이터를 가장 먼저 삭제한다는 점이 특징이다. 우선순위 큐는 데이터를 우선순위에 따라 처리하고 싶을 때 사용한다. 여러 개의 값들 중에서 최대값이나 최솟값을 빠르게 찾아내도록 만들어진 자료구조이다. 대표적으로 최댓값 추출을 보면 예를 들어 큐에 [1, 4, 5, 3, 2] 가 들어 있고 최댓값을 추출하는 우선순위 큐가 있다고 가정하자. 이 경우 항상 남아 잇는 요소들의 최댓값이 우선순위에 따라 추출되어 5, 4, 3, 2, 1 순으로 추출된다. 

 

힙의 특징

힙에서 착각하기 쉬운 특징은, 힙은 정렬된 구조가 아니라는 점이다. 최소 힙의 경우 부모 노드가 항상 작다는 조건만 만족할 뿐, 서로 정렬되어 있지 않다. 예를 들어 오른쪽의 자식 노드가 더 커야될 것 같지만 왼쪽 노드보다 작은 경우도 얼마든지 있을 수 있다. 부모, 자식 간의 관계만 정의할 뿐, 좌우에 대한 관계는 정의하지 않기 때문이다. 

 

자식이 둘인 힙은 특별히 이진 힙(Binary Heap)이라 하며, 대부분은 이진 힙이 널리 사용된다. 힙을 저장하는 표준적인 자료구조는 배열이다. 구현을 쉽게 하기 위해 첫 번째 인덱스인 0은 사용되지 않는다. 힙은 여러 알고리즘에서 사용되는데 우선 순위 큐 뿐만 아니라 다익스트라 알고리즘, 최소 신장 트리를 구현하는 프림 알고리즘 등에도 활용된다. 

 

728x90
반응형

'알고리즘 > 알고리즘 이론' 카테고리의 다른 글

최단경로 (다익스트라)  (0) 2021.05.04
DFS / BFS  (0) 2021.04.12
정렬(Sort)  (0) 2021.04.12
재귀 함수  (0) 2021.04.02
브루트 포스 (Brute Force)  (0) 2021.03.18
728x90
반응형

www.acmicpc.net/problem/7576

 

7576번: 토마토

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토

www.acmicpc.net

문제

철수의 토마토 농장에서는 토마토를 보관하는 큰 창고를 가지고 있다. 토마토는 아래의 그림과 같이 격자 모양 상자의 칸에 하나씩 넣어서 창고에 보관한다. 

창고에 보관되는 토마토들 중에는 잘 익은 것도 있지만, 아직 익지 않은 토마토들도 있을 수 있다. 보관 후 하루가 지나면, 익은 토마토들의 인접한 곳에 있는 익지 않은 토마토들은 익은 토마토의 영향을 받아 익게 된다. 하나의 토마토의 인접한 곳은 왼쪽, 오른쪽, 앞, 뒤 네 방향에 있는 토마토를 의미한다. 대각선 방향에 있는 토마토들에게는 영향을 주지 못하며, 토마토가 혼자 저절로 익는 경우는 없다고 가정한다. 철수는 창고에 보관된 토마토들이 며칠이 지나면 다 익게 되는지, 그 최소 일수를 알고 싶어 한다.

토마토를 창고에 보관하는 격자모양의 상자들의 크기와 익은 토마토들과 익지 않은 토마토들의 정보가 주어졌을 때, 며칠이 지나면 토마토들이 모두 익는지, 그 최소 일수를 구하는 프로그램을 작성하라. 단, 상자의 일부 칸에는 토마토가 들어있지 않을 수도 있다.

입력

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토들의 정보가 주어진다. 즉, 둘째 줄부터 N개의 줄에는 상자에 담긴 토마토의 정보가 주어진다. 하나의 줄에는 상자 가로줄에 들어있는 토마토의 상태가 M개의 정수로 주어진다. 정수 1은 익은 토마토, 정수 0은 익지 않은 토마토, 정수 -1은 토마토가 들어있지 않은 칸을 나타낸다.

토마토가 하나 이상 있는 경우만 입력으로 주어진다.

출력

여러분은 토마토가 모두 익을 때까지의 최소 날짜를 출력해야 한다. 만약, 저장될 때부터 모든 토마토가 익어있는 상태이면 0을 출력해야 하고, 토마토가 모두 익지는 못하는 상황이면 -1을 출력해야 한다.

 

예제 입력1

6 4
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 1

예제 출력1

8

 

예제 입력2

6 4
0 -1 0 0 0 0
-1 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 1

예제 출력2

-1

 

 

[문제 해설]

문제를 해석하면 1은 익은 토마토, 0은 익지 않은 토마토 -1은 토마토가 없는 곳이다. 하루가 지나면 1 옆에 0은 1로 변한다. (옆이라는 것은 대각선이 아닌 가로, 세로로 인접한 곳을 의미) 

 

dx,dy를 통해 상하좌우를 탐색해야 되므로 bfs를 떠올렸다. 일단 bfs로 근처 0이 1로 변하는 것을 짜고 정답인 걸리는 시간은 나중에 생각해야 겠다고 결정했다.

 

입력을 받고, bfs에 사용되는 큐에는 토마토 중 익은것인 1이 되어있는 것을 넣어주었다.

from collections import deque
m,n = map(int,input().split())
tomato=[]
for i in range(n):
    tomato.append(list(map(int,input().split())))

queue=deque()
for i in range(n):
    for j in range(m):
        if tomato[i][j]==1:
            queue.append((i,j))

(bfs에서는 deque를 사용하므로 collections import해주었다.)

 

그리고 bfs를 사용해서 1. 범위를 넘어간 곳이면 continue 2. 0인 곳을 만나면 1로 바꾸어주고 큐에 넣어줌. 3. -1인 곳도 그냥 continue

from collections import deque
m,n = map(int,input().split())
tomato=[]
for i in range(n):
    tomato.append(list(map(int,input().split())))

queue=deque()
for i in range(n):
    for j in range(m):
        if tomato[i][j]==1:
            queue.append((i,j))
            
dx = [-1, 1, 0, 0]  # 상, 하, 좌, 우
dy = [0, 0, -1, 1]
while queue:
    x,y=queue.popleft()
    for i in range(4):
        nx=x+dx[i]
        ny=y+dy[i]
        if nx<0 or nx>=n or ny<0 or ny>=m:
            continue
        if tomato[nx][ny]==0:
            tomato[nx][ny]=1
            queue.append((nx, ny))
        if tomato[nx][ny] == -1:
            continue

 

그리고 이제 여기까지 되었으면 마지막 단계인 토마토가 다익는데 걸리는 기간을 확인해야 된다. 여기서 떠올린 생각은 토마토가 완성되는데 걸리는 시간은 큐에 원소의 갯수에 따라 달라질 수 있다는 것이다. 

만약 처음에 tomato맵에서 1이었던 것이 2개이면 동시에 bfs함수를 돌려서 그날에 익는 갯수를 확인해야 될 것이다. 그러므로 for문을 통해 tomato맵에서 1이었던 것의 갯수만큼만 반복해주면 된다.

(이미 한것에 한해서는 bfs함수를 해서 주위엔 익은 상태이기 때문에 신경을 안써주어도 되기 때문)

 

[최종 정답]

from collections import deque

m, n = map(int, input().split())
tomato = []
for _ in range(n):
    tomato.append(list(map(int, input().split())))
# 1은 익은 토마토, 정수 0은 익지 않은 토마토, 정수 -1은 토마토가 들어있지 않음
dx = [-1, 1, 0, 0]  # 상, 하, 좌, 우
dy = [0, 0, -1, 1]
queue = deque()
for i in range(n):
    for j in range(m):
        if tomato[i][j]==1:
            queue.append((i, j))
day = 1
count = len(queue)
while queue:
    for i in range(count):
        x, y = queue.popleft()
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            if nx<0 or nx>=n or ny<0 or ny>=m:
                continue
            if tomato[nx][ny] == 0:
                tomato[nx][ny] = 1
                queue.append((nx, ny))
            if tomato[nx][ny] == -1:
                continue
    day+=1
    count=len(queue)
answer=0
for i in range(n):
    for j in range(m):
        if tomato[i][j]==0:
            answer=-1
if answer!=-1:
    answer=day

print(answer)
728x90
반응형
728x90
반응형

www.acmicpc.net/problem/1697

 

1697번: 숨바꼭질

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일

www.acmicpc.net

문제

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때 걷는다면 1초 후에 X-1 또는 X+1로 이동하게 된다. 순간이동을 하는 경우에는 1초 후에 2*X의 위치로 이동하게 된다.

수빈이와 동생의 위치가 주어졌을 때, 수빈이가 동생을 찾을 수 있는 가장 빠른 시간이 몇 초 후인지 구하는 프로그램을 작성하시오.

입력

첫 번째 줄에 수빈이가 있는 위치 N과 동생이 있는 위치 K가 주어진다. N과 K는 정수이다.

출력

수빈이가 동생을 찾는 가장 빠른 시간을 출력한다.

 

예제 입력 1

5 17

예제 출력 

4

힌트

수빈이가 5-10-9-18-17 순으로 가면 4초만에 동생을 찾을 수 있다.

 

[문제 해설]

처음 생각했던 것은 다이나믹 프로그래밍이였다. 각각의 위치에 가는 시간을 배열에 저장해서 다이나믹 프로그래밍으로 풀려고 했으나 생각보다 어려웠던 것은 뒤로 가는 경우와 앞으로 가는 경우까지 생각해야되서 복잡했다. 

 

돌고 돌아 BFS로 풀게 되었다. 모든 장소를 0으로 초기화 시킨 배열 d도 준비한다. 출발점을 큐의 첫번째 원소로 먼저 넣는다. 그 점에서 -1, +1, *2 한 곳을 next로 두고 for문을 통해서 반복한다. next가 범위 안에 있고, d배열에서 방문하지 않은 0이면 d[next]는 d[v]+1값이 들어가고 next를 큐에 추가해준다. 그럼 n에서 k로 가는 최소점이 d배열에 들어가게 된다. 종료 조건은 큐에 k값이 들어간게 확인되었을때 d[v]를 출력해주고 종료하면 된다.

 

(나는 d배열에 어떤값이 들어가있는지 계속 헷갈려서 print로 찍어주었다. 주석은 무시하고 보면 된다_

from collections import deque
n, k = map(int, input().split())
# n에서 k로 가기
d=[0]*100001
def bfs(n):
    queue=deque()
    queue.append(n)
    while queue:
        v=queue.popleft()
        if v==k:
            print(d[v])
            return
        for next in (v-1,v+1,v*2):
            if 0<=next<100001 and not d[next]:
                # for i in range(20):
                #     print(d[i], end=' ')
                # print()
                d[next]=d[v]+1
                queue.append(next)

bfs(n)
728x90
반응형
728x90
반응형

1. relative

position:relative

이 코드는 원래 자리를 기준으로 위치를 잡아 포지셔닝 하는 것이다. 겹치게 할 때 유용하게 쓸 수 있다. 이 코드를 쓰면 원래 위치에서 상대적인 곳으로 이동한다. top, bottom, left, right 를 써서 조절할 수 있다.

ex>

position:relative
top:80px
left:30px

 

2.fixed

position:fixed;

fixed포지션은 브라우저를 기준으로 포지셔닝 해준다. top:30px을 하면 브라우저의 위에서 30px을 해준다.

스크롤을 해도 똑같은 자리에 있게 된다.

 

fixed 포지션은 스크롤해도 계속 같은 자리를 유지하고 싶을때 사용한다.

 

3.absolute

요소들은 기본적으로 static포지션이다. (포지셔닝이 안 된 요소)

포지셔닝이 된것은 relative, fixed, absolute포지션이다.

absolute포지셔닝은 가장 가까운 포지셔닝이 된 조상 요소가 기준이다.

728x90
반응형

'프론트엔드 > HTML CSS' 카테고리의 다른 글

display 속성  (0) 2021.04.11
CSS 활용하기  (0) 2021.04.11
웹에서의 박스 모델(Box Model) 다루기  (0) 2021.04.09
HTML, CSS, JavaScript  (0) 2021.04.02
728x90
반응형

탐색Search이란 많은 양의 데이터 중에서 원하는 데이터를 찾는 과정을 의미한다. 대표적인 탐색 알고리즘이 DFS와 BFS이다. 프로그래밍에서는 자료구조 안에서 탐색을 하는데 DFS와 BFS를 알려면 스택과 큐에 관한 자료구조를 알아야된다.

 

스택

스택Stack은 레고 쌓기에 비유할 수 있다. 레고는 아래에서부터 위로 차곡차곡 쌓는다. 맨 아래에 레고를 빼기 위해서는 위에 레고를 빼야 된다. 즉, 먼저 쌓은 것이 제일 나중에 뺄 수 있다. 선입후출(First In Last Out) 또는 후입선출(Last In First Out) 구조 라고 한다. 

 

stack=[]
#삽입(1) - 삭제() - 삽입(3) - 삽입(5) - 삽입(6) - 삭제() - 삽입(2) - 삭제()
stack.append(1)
stack.pop()
stack.append(3)
stack.append(5)
stack.append(6)
stack.pop()
stack.append(2)
stack.pop()
print(stack)#[3,5]
print(stack[::-1])#[5,3]

 

append() 메서드는 리스트의 가장 뒤쪽에 데이터를 삽입하고, pop() 메서드는 리스트의 가장 뒤쪽에서 데이터를 꺼낸다.


 

큐Queue는 대기 줄에 비유할 수 있다. 우리가 놀이공원에 입장하기 위해 줄을 설 때, 먼저 온 사람이 먼저 들어가게 된다. 나중에 온 사람이 나중에 들어가기 때문에 흔히 '공정한' 자료구조라고 비유된다. 이러한 구조를 선입선출(First In First Out)라고 한다.

 

from collections import deque
queue=deque()
#삽입(4) - 삽입(2) - 삽입(1) - 삽입(8) - 삭제() - 삭제() - 삽입(4)
queue.append(4)
queue.append(2)
queue.append(1)
queue.append(8)
queue.popleft()
queue.popleft()
queue.append(4)

print(queue)
queue.revers()
print(queue)

 

파이썬으로 큐를 구현할 때는 collections 모듈에서 제공하는 deque 자료구조를 활용한다. deque는 스택과 큐의 장점을 모두 채택한 것인데 데이터를 넣고 빼는 속도가 리스트 자료형에 비해 효율적이며 queue 라이브러리를 이용하는 것보다 더 간단하다.

deque객체를 자료형으로 변경하고자 한다면 list(queue)를 사용하면 리스트 자료형이 반환된다.


DFS

DFS는 Depth-First Search, 깊이 우선 탐색이라고도 부르며, 그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘이다. DFS를 알려면 그래프의 표현 방식을 알아야 된다. 

 

그래프는 2가지 방식으로 표현할 수 있는데 인접 행렬 방식과 인접 리스트 방식이 있다.

인접 행렬 방식은 2차원 배열로 그래프의 연결 관계를 표현하는 방식이고 인접 리스트 방식은 리스트로 그래프의 연결 관계를 표현하는 방식이다. 

 

인접 행렬 방식은 2차원 배열에 각 노드가 연결된 형태를 기록하는 방식이다. 연결이 되어 있지 않은 노드끼리는 무한Infinity의 비용이라고 작성한다. 

 

인접 행렬 방식 예제

INF = 9999999999

graph = [
    [0,7,5],
    [7,0,INF],
    [5,INF,0]
]

print(graph)

[[0,7,5],[7,0,99999999],[5,999999999,0]]

 

 인접 리스트 방식에서는 모든 노드에 연결된 노드에 대한 정보를 차례대로 연결하여 저장한다. C++이나 Java와 같은 프로그래밍 언어에서는 별도로 연결 리스트 기능을 위한 표준 라이브러리를 제공한다.

 반면에 파이썬은 기본 자료형인 리스트 자료형이 append()와 메소드를 제공하므로, 전통적인 프로그래밍 언어에서의 배열과 연결 리스트의 기능을 모두 기본으로 제공한다. 파이썬으로 인접 리스트를 이용해 그래프를 표현하고자 할 때에도 단순히 2차원 리스트를 이용하면 된다. 

 

# 행(ROW)이 3개인 2차원 리스트로 인접 리스트 표현
graph = [[] for _ in range(3)]

#노드 0에 연결된 노드 정보 저장(노드, 거리)
graph[0].append((1,7))
graph[0].append((2,5))

#노드 1에 연결된 노드 정보 저장(노드, 거리)
graph[1].append((0,7))

#노드 2에 연결된 노드 정보 저장(노드, 거리)
graph[2].append((0,5))

print(graph)

 

DFS는 어떻게 작동할까? DFS는 깊이 우선 탐색 알고리즘이다. 이 알고리즘은 특정한 경로로 탐색하다가 특정한 상황에서 최대한 깊숙이 들어가서 노드를 방문한 후, 다시 돌아가 다른 경로로 탐색하는 알고리즘이다. 

 

1. 탐색 시작 노드를 스택에 삽입하고 방문 처리를 한다.

2. 스택의 최상단 노드에 방문하지 않은 인접 노드가 있으면 그 인접 노드를 스택에 넣고 방문 처리를 한다. 방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼낸다. 

3. 2번의 과정을 더 이상 수행할 수 없을 때까지 반복한다.

 

방문 처리는 스택에 한 번 삽입되어 처리된 노드가 다시 삽입되지 않게 체크하는 것을 의미한다. 방문 처리를 함으로써 각 노드를 한 번씩만 처리할 수 있다.

 

깊이 우선 탐색 알고리즘인 DFS는 스택 자료구조에 기초한다는 점에서 구현이 간단하다. 데이터의 개수가 N개인 경우 O(N)의 시간이 소요된다는 특징이 있다. 

 

또한 DFS는 스택을 이용하는 알고리즘이기 때문에 실제 구현은 재귀 함수를 이용했을 때 매우 간결하게 구현할 수 있다. 

 

def dfs(graph, v, visited):
    #현재 노드를 방문 처리
    visited[v]=True
    print(v,end=" ")
    #현재 노드와 연결된 다른 노드를 재귀적으로 방문
    for i in graph[v]:
        if not visited[i]:
            dfs(graph,i,visited)


#각 노드가 연결된 정보를 리스트 자료형으로 표현(2차원 리스트)
graph= [
    [],
    [2,3,8],
    [1,7],
    [1,4,5],
    [3,5],
    [3,4],
    [7],
    [2,6,8],
    [1,7]
]

#각 노드가 방문된 정보를 리스트 자료형으로 표현
visited = [False]*9

#정의된 DFS 함수 호출
dfs(graph,1,visited)

#1 2 7 6 8 3 4 5

BFS

BFS(Breadth First Search) 알고리즘은 '너비 우선 탐색'이라는 의미를 가진다. BFS는 가까운 노드부터 탐색하는 알고리즘이다. DFS는 최대한 멀리 있는 노드를 우선으로 탐색하는 방식으로 동작한다고 했는데, BFS는 그 반대로 가까이 있는것부터 차근차근 탐색해나간다. 

 

BFS 구현에서는 선입선출 방식인 큐 자료구조를 이용한다. 인접한 노드를 반복적으로 큐에 넣어서 가까운 노드부터 탐색해서 탐색할 수 있을때까지 찾아나가는 것이다.

 

1. 탐색 시작 노드를 큐에 삽입하고 방문 처리를 한다.

2. 큐에서 노드를 꺼내 해당 노드의 인접 노드 중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문 처리를 한다.

3. 2번의 과정을 더 이상 수행할 수 없을 때까지 반복한다.

 

BFS는 큐 자료구조에 기초한다. 구현함에 있어 deque라이브러리를 사용하는 것이 좋으며 탐색을 수행함에 있어 O(N)의 시간이 소요된다. 일반적인 경우 실제 수행 시간은 DFS보다 좋은 편이다.

 

from collections import deque

graph=[
    [],
    [2,3,8],
    [1,7],
    [1,4,5],
    [3,5]
]
visited=[False]*9

def bfs(graph,start,visited):
    queue=deque([start])
    visited[start]=True
    while queue:
        #큐에서 하나의 원소를 뽑아 출력
        v=queue.popleft()
        print(v,end=' ')
        #해당 원소와 연결된, 아직 방문하지 않은 원소들을 큐에 삽입
        for i in graph[v]:
            if not visited[i]:
                queue.append(i)
                visited[i]=True

 

2차원 배열에서의 탐색 문제를 만나면 그래프 형태로 바꿔서 생각하면 풀이 방법을 조금 더 쉽게 떠올릴 수 있다.

728x90
반응형

'알고리즘 > 알고리즘 이론' 카테고리의 다른 글

최단경로 (다익스트라)  (0) 2021.05.04
힙 자료구조  (0) 2021.05.04
정렬(Sort)  (0) 2021.04.12
재귀 함수  (0) 2021.04.02
브루트 포스 (Brute Force)  (0) 2021.03.18

+ Recent posts