728x90

p263

전보 문제

# 이코테 p262
# 전보
import sys
import heapq
INF = int(1e9)
n, m, c = map(int, sys.stdin.readline().split())
graph = [[] for i in range(n+1)]
for _ in range(m):
    x, y, z = map(int,sys.stdin.readline().split())
    graph[x].append((y,z))
distance = [INF] * (n+1)

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]:
            y, z = i[0], i[1] #y가 노드 z가 비용
            cost = dist + z
            if cost < distance[y]:
                distance[y] = cost
                heapq.heappush(q, (cost, y))
dijkstra(c)
cnt = 0
max_distance = 0
for d in distance:
    if d != INF:
        cnt += 1
        max_distance = max(max_distance, d)
print(cnt-1, max_distance)
728x90

문제

n(2 ≤ n ≤ 100)개의 도시가 있다. 그리고 한 도시에서 출발하여 다른 도시에 도착하는 m(1 ≤ m ≤ 100,000)개의 버스가 있다. 각 버스는 한 번 사용할 때 필요한 비용이 있다.

모든 도시의 쌍 (A, B)에 대해서 도시 A에서 B로 가는데 필요한 비용의 최솟값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 도시의 개수 n이 주어지고 둘째 줄에는 버스의 개수 m이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스의 출발 도시의 번호가 주어진다. 버스의 정보는 버스의 시작 도시 a, 도착 도시 b, 한 번 타는데 필요한 비용 c로 이루어져 있다. 시작 도시와 도착 도시가 같은 경우는 없다. 비용은 100,000보다 작거나 같은 자연수이다.

시작 도시와 도착 도시를 연결하는 노선은 하나가 아닐 수 있다.

출력

n개의 줄을 출력해야 한다. i번째 줄에 출력하는 j번째 숫자는 도시 i에서 j로 가는데 필요한 최소 비용이다. 만약, i에서 j로 갈 수 없는 경우에는 그 자리에 0을 출력한다.

코드

# 11401
import sys
INF = int(1e9)
n = int(sys.stdin.readline()) # 도시 (노드)
m = int(sys.stdin.readline()) # 버스 (간선)
graph = [[INF]*(n+1) for _ in range(n+1)] # 최단경로 저장

# 자기 자신 지나는 경로 0으로 초기화
for a in range(1,n+1):
    for b in range(1,n+1):
        if a==b:
            graph[a][b] = 0

for _ in range(m):
    a, b, c = map(int,sys.stdin.readline().split())
    # 시작 도시와 도착 도시를 연결하는 노선은 하나가 아닐 수 있다.
    # graph[a][b] = c
    graph[a][b] = min(graph[a][b],c)

# 플로이드 워셜 수행
for k in range(1, n+1):
    for a in range(1, n+1):
        for b in range(1, n+1):
            graph[a][b] = min(graph[a][b], graph[a][k]+graph[k][b])

# 최단경로 출력
for a in range(1, n+1):
    for b in range(1, n+1):
        if graph[a][b] == INF: # 갈 수 없다면 0으로 출력
            print(0, end=" ")
        else:
            print(graph[a][b], end=" ")
    print()

풀이

시작 도시와 도착 도시를 연결하는 노선이 하나가 아닐 수 있다는 것에 주의해서

플로이드 워셜 알고리즘을 수행하면 된다.

728x90

문제

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다.

연구소는 크기가 N×M인 직사각형으로 나타낼 수 있으며, 직사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다. 

일부 칸은 바이러스가 존재하며, 이 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다. 새로 세울 수 있는 벽의 개수는 3개이며, 꼭 3개를 세워야 한다.

예를 들어, 아래와 같이 연구소가 생긴 경우를 살펴보자.

2 0 0 0 1 1 0
0 0 1 0 1 2 0
0 1 1 0 1 0 0
0 1 0 0 0 0 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

이때, 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 곳이다. 아무런 벽을 세우지 않는다면, 바이러스는 모든 빈 칸으로 퍼져나갈 수 있다.

2행 1열, 1행 2열, 4행 6열에 벽을 세운다면 지도의 모양은 아래와 같아지게 된다.

2 1 0 0 1 1 0
1 0 1 0 1 2 0
0 1 1 0 1 0 0
0 1 0 0 0 1 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

바이러스가 퍼진 뒤의 모습은 아래와 같아진다.

2 1 0 0 1 1 2
1 0 1 0 1 2 2
0 1 1 0 1 2 2
0 1 0 0 0 1 2
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

벽을 3개 세운 뒤, 바이러스가 퍼질 수 없는 곳을 안전 영역이라고 한다. 위의 지도에서 안전 영역의 크기는 27이다.

연구소의 지도가 주어졌을 때 얻을 수 있는 안전 영역 크기의 최댓값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. (3 ≤ N, M ≤ 8)

둘째 줄부터 N개의 줄에 지도의 모양이 주어진다. 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 위치이다. 2의 개수는 2보다 크거나 같고, 10보다 작거나 같은 자연수이다.

빈 칸의 개수는 3개 이상이다.

출력

첫째 줄에 얻을 수 있는 안전 영역의 최대 크기를 출력한다.

코드

# 14502
import sys
from collections import deque
import copy

def bfs():
    q = deque()
    tmp_graph = copy.deepcopy(graph) # 벽 설치 그래프 깊은 복사
    for i in range(N):
        for j in range(M):
            if tmp_graph[i][j] == 2: # 바이러스 있는 좌표만 큐에 넣기
                q.append((i,j))
    while q:
        x, y = q.popleft()
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            if 0 <= nx < N and 0 <= ny < M and tmp_graph[nx][ny] == 0:
                tmp_graph[nx][ny] = 2
                q.append((nx, ny))
    # 안전영역 ( 0인 칸 세기 )
    cnt0 = 0
    global answer
    for i in range(N):
        cnt0 += tmp_graph[i].count(0)
    answer = max(answer, cnt0)


def newwall(cnt):
    if cnt == 3:
        bfs() #벽 3개 새롭게 만들면 bfs 진행
        return #재귀함수로 돌아가서 벽 없애고 다시 재귀함수 
    for i in range(N):
        for j in range(M):
            if graph[i][j] == 0:
                graph[i][j] = 1 # 빈 칸이면 벽 설치
                newwall(cnt+1) # 벽 설치 재귀함수
                graph[i][j] = 0

N, M = map(int,sys.stdin.readline().split())
graph = [list(map(int,sys.stdin.readline().split())) for _ in range(N)]
dx = [0, -1, 0, 1]
dy = [-1, 0, 1, 0]
answer = 0
newwall(0) #벽 만들기
print(answer)

풀이

바이러스(2) 주변에서 빈칸(0) 중 3개를 선택해 벽(1)을 세우고 bfs로 바이러스가 퍼질 때, 0의 개수 중 최댓값을 구한다.

벽을 3개 세우는 모든 경우의 수에 bfs를 실행시켜 최대값을 구해야 한다.

이때, 경우의 수마다 bfs 실행 시, copy 라이브러리의 deepcopy를 사용한다.

728x90

문제

이 문제는 아주 평범한 배낭에 관한 문제이다.

한 달 후면 국가의 부름을 받게 되는 준서는 여행을 가려고 한다. 세상과의 단절을 슬퍼하며 최대한 즐기기 위한 여행이기 때문에, 가지고 다닐 배낭 또한 최대한 가치 있게 싸려고 한다.

준서가 여행에 필요하다고 생각하는 N개의 물건이 있다. 각 물건은 무게 W와 가치 V를 가지는데, 해당 물건을 배낭에 넣어서 가면 준서가 V만큼 즐길 수 있다. 아직 행군을 해본 적이 없는 준서는 최대 K만큼의 무게만을 넣을 수 있는 배낭만 들고 다닐 수 있다. 준서가 최대한 즐거운 여행을 하기 위해 배낭에 넣을 수 있는 물건들의 가치의 최댓값을 알려주자.

입력

첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)가 주어진다.

입력으로 주어지는 모든 수는 정수이다.

출력

한 줄에 배낭에 넣을 수 있는 물건들의 가치합의 최댓값을 출력한다.

코드

# 12865
# 냅색 알고리즘
import sys
n, k = map(int,sys.stdin.readline().split())
knapsack = [[0 for _ in range(k+1)] for _ in range(n+1)]
stuff = [[0,0]]
for _ in range(n):
    stuff.append(list(map(int,sys.stdin.readline().split())))

for i in range(n+1):
    for j in range(k+1):
        weight = stuff[i][0]
        value = stuff[i][1]

        if j < weight: # 버틸 수 있는 무게가 현재 무게보다 작다면 전에 있는 값 그대로
            knapsack[i][j] = knapsack[i-1][j]
        else:
            # max( 현재 물건을 넣어주고 남은 무게를 채울 수 있는 최대값 / 다른 물건들로 채우는 값 )
            knapsack[i][j] = max(value + knapsack[i-1][j-weight], knapsack[i-1][j])
print(knapsack[n][k])

풀이

냅색 알고리즘이다. 

 

현재 물건이 현재 돌고 있는 무게보다 작다면,

바로 [이전물건][같은무게] 값을 가져온다.

현재 물건이 현재 돌고 있는 무게보다 크다면,

max(현재 물건을 넣어주고 남은 무게를 채울 수 있는 최대값, 다른 물건으로 채우는 값) 을 knapsack[i][j]에 삽입한다.

 

최종적으로 knapsack[n][k] 출력하면 무게가 k일 때의 최대 값을 출력할 수 있다.

728x90

문제

세계적인 도둑 상덕이는 보석점을 털기로 결심했다.

상덕이가 털 보석점에는 보석이 총 N개 있다. 각 보석은 무게 Mi와 가격 Vi를 가지고 있다. 상덕이는 가방을 K개 가지고 있고, 각 가방에 담을 수 있는 최대 무게는 Ci이다. 가방에는 최대 한 개의 보석만 넣을 수 있다.

상덕이가 훔칠 수 있는 보석의 최대 가격을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N과 K가 주어진다. (1 ≤ N, K ≤ 300,000)

다음 N개 줄에는 각 보석의 정보 Mi와 Vi가 주어진다. (0 ≤ Mi, Vi ≤ 1,000,000)

다음 K개 줄에는 가방에 담을 수 있는 최대 무게 Ci가 주어진다. (1 ≤ Ci ≤ 100,000,000)

모든 숫자는 양의 정수이다.

출력

첫째 줄에 상덕이가 훔칠 수 있는 보석 가격의 합의 최댓값을 출력한다.

코드

import sys
import heapq
n, k = map(int,sys.stdin.readline().split())
q = [] # 보석 최소힙
bag = [] #가방 무게 리스트
for _ in range(n):
    heapq.heappush(q,list(map(int,sys.stdin.readline().split())))
for _ in range(k):
    bag.append(int(sys.stdin.readline()))
# 가방 오름차순 정렬
bag.sort()

result = 0
take = [] # 담은거
for c in bag:
    # 가방에 담을 수 있는 무게만 최대힙에 넣기
    while q and q[0][0] <= c:
        possible_m, possible_v = heapq.heappop(q)
        heapq.heappush(take, -possible_v)

    if take:
        result += (-heapq.heappop(take))
    elif not q:
        break
print(result)

힌트

두 번째 예제의 경우 첫 번째 보석을 두 번째 가방에, 세 번째 보석을 첫 번째 가방에 넣으면 된다.

풀이

우선순위 큐를 이용한 그리디 알고리즘.

백준 사이트에 있는 힌트를 보면 왜 가방을 오름차순으로 정렬해서 작은 가방에 보석을 먼저 넣어야 하는지 알 수 있다.

가장 작은 가방부터 담을 수 있는 무게의 보석 중 가장 큰 값어치를 하는 보석을 넣으면 된다.

 

최대 힙, 최소 힙으로 풀면 쉽게 풀린다.

 
728x90

문제

성경이는 트럭을 정글 속에서 운전하다가 트럭의 연료탱크에 갑자기 구멍이 나서 1km를 가는데 1L의 연료가 새 나가게 되었다. 이것을 고치기 위해서는 가장 가까운 마을에 가야 한다. 그런데 그냥 가다가는 중간에 연료가 다 빠질 수가 있다. 다행스럽게도 정글 곳곳에 연료를 채울 수 있는 주유소가 N개 있다. 그런데 정글 속에서 중간에 차를 멈추는 행위는 매우 위험한 행위이므로 주유소에서 멈추는 횟수를 최소화 하려 한다.

그리고 다행이도 성경이의 트럭은 매우 좋아서 연료 탱크에는 연료의 제한이 없이 많이 충분히 많이 넣을 수 있다고 한다. 각각의 주유소의 위치와, 각 주유소에서 얻을 수 있는 연료의 양이 주어져 있을 때, 주유소에서 멈추는 횟수를 구하는 프로그램을 작성하시오.

정글은 일직선이고, 성경이의 트럭과 주유소도 모두 일직선 위에 있다. 주유소는 모두 성경이의 트럭을 기준으로 오른쪽에 있다.

입력

첫째 줄에 주유소의 개수 N(1 ≤ N ≤ 10,000)가 주어지고 두 번째 줄부터 N+1번째 줄 까지 주유소의 정보가 주어진다. 주유소의 정보는 두개의 정수 a,b로 이루어 져 있는데 a(1 ≤ a ≤ 1,000,000)는 성경이의 시작 위치에서 주유소 까지의 거리, 그리고 b(1 ≤ b ≤ 100)는 그 주유소에서 채울 수 있는 연료의 양을 의미한다. 그리고 N+2번째 줄에는 두 정수 L과 P가 주어지는데 L(1 ≤ L ≤ 1,000,000)은 성경이의 위치에서 마을까지의 거리, (1 ≤ P ≤ 1,000,000)는 트럭에 원래 있던 연료의 양을 의미한다.

출력

첫째 줄에 주유소에서 멈추는 횟수를 출력한다. 만약 마을에 도착하지 못할경우 -1을 출력한다.

코드

import sys
import heapq
n = int(sys.stdin.readline())
q = []
for _ in range(n):
    heapq.heappush(q,list(map(int,sys.stdin.readline().split())))
    # 최소힙에 삽입
target, fuel = map(int,sys.stdin.readline().split())

move = [] #움직인 큐 최대힙
cnt = 0
while fuel<target:
    while q and q[0][0]<=fuel:
        mf, pf = heapq.heappop(q)
        heapq.heappush(move,[-pf,mf])

    if not move:
        cnt = -1
        break
    pf1, mf1 = heapq.heappop(move)
    fuel += -pf1
    cnt += 1
print(cnt)

풀이

그리디 알고리즘, 우선순위 큐

 

1km를 가는데 1L의 연료가 새 나가게 되니까 처음의 연료양으로 최종 마을까지 가야 한다.

  1. 입력받은 주유소 n개의 ( 주유소 거리, 연료 양 )을 최소 힙에 저장한다.
  2. 처음의 연료 양을 시작으로 최종 마을까지 최소의 주유소를 거쳐 가야 한다.
  3. 가는 길에 주유소가 있어야 하고 갈 수 있는 주유소에서 최대의 연료 양을 뽑아야 한다.
  4. 이때, 우선순위 큐를 생각하여 최대 힙으로 바꿔야 한다.
  5. 그중에서도 연료 양이 기준이 되기에 (연료 양, 주유소 거리) 순서를 바꿔주고 ( - 연료양, 주유소거리 )로 연료 양에 -를 붙여서 최대 힙을 만든다. 

 

 

출처 : 백준

728x90

문제

2차원 격자공간에 두 개의 꼭짓점 좌표로 표현되는 직사각형이 있다. 직사각형은 아래와 같이 왼쪽 아래 꼭짓점 좌표 (x, y)와 오른쪽 위 꼭짓점 좌표 (p, q)로 주어진다.

이 문제에서 모든 직사각형은 두 꼭짓점의 좌표를 나타내는 4개의 정수 x y p q 로 표현된다. 단 항상 x<p, y<q 이다. 예를 들어 위 그림에 제시된 직사각형이라면 아래와 같이 표현된다.

3 2 9 8

두 개의 직사각형은 그 겹치는 부분의 특성에 따라 다음 4가지 경우로 분류될 수 있다. 

먼저 두 직사각형의 겹치는 부분이 직사각형인 경우이다. 아래 그림(a)는 공통부분이 직사각형인 경우의 3가지 예를 보여준다,

그림 (a)

또는 겹치는 부분이 아래 그림 (b)와 같이 선분이 될 수도 있고, 그림 (c)와 같이 점도 될 수 있다. 

그림 (b)

그림 (c)

마지막으로 아래 그림 (d)와 같이 공통부분 없이 두 직사각형이 완전히 분리된 경우도 있다.

그림 (d)

여러분은 두 직사각형의 겹치는 부분이 직사각형인지, 선분인지, 점인지, 아니면 전혀 없는 지를 판별해서 해당되는 코드 문자를 출력해야 한다. 

공통부분의 특성코드 문자
직사각형 a
선분 b
c
공통부분이 없음 d

입력

4개의 줄로 이루어져 있다. 각 줄에는 8개의 정수가 하나의 공백을 두고 나타나는데, 첫 4개의 정수는 첫 번째 직사각형을, 나머지 4개의 정수는 두 번째 직사각형을 각각 나타낸다. 단 입력 직사각형의 좌표 값은 1이상 50,000 이하의 정수로 제한된다. 

출력

4개의 각 줄에 주어진 두 직사각형의 공통부분을 조사해서 해당하는 코드 문자를 출력파일의 첫 4개의 줄에 각각 차례대로 출력해야 한다.

코드1

#2527
import sys
for i in range(4):
    x1, y1, x2, y2, x3, y3, x4, y4 = map(int,sys.stdin.readline().split())
    # 직사각형이 겹치기 위한 계산
    # 왼쪽 변
    xl = max(x1, x3)
    # 오른쪽 변
    xr = min(x2, x4)
    # 윗 변
    upr = min(y2,y4)
    # 아랫 변
    downl = max(y1, y3)

    #겹치는 부분 (차이)
    diffx = xr-xl
    diffy = upr-downl

    # 차이가 둘다 양수면 겹치는 부분이 직사각형
    if diffx > 0 and diffy > 0:
        print("a")
    # 둘 다 0이면 점
    elif diffx == 0 and diffy == 0:
        print("c")
    # 공통부분이 없음
    elif diffx < 0 or diffy < 0:
        print("d")
    else: # 선분
        print("b")

코드2

#2527
import sys
for i in range(4):
    x1, y1, p1, q1, x2, y2, p2, q2 = map(int,sys.stdin.readline().split())

    if p1 < x2 or q1 < y2 or p2 < x1 or q2 < y1:
        #겹치지 않음
        print("d")
        continue
    elif p1 == x2 or p2 == x1:
        if q1 == y2 or q2 == y1:
            print("c") #점
            continue
        else:
            print("b") #선분
            continue
    elif q1 == y2 or q2 == y1:
        print("b")
        continue
    else:
        print("a") #직사각형
        continue

+ Recent posts