03 Mar 2020
|
Python
USACO
PS
USACO 2015 February
1. Censoring
글자에서 해당 문자가 나타나는지 확인 후, 나타나면 삭제하는 문제. 문제에서 주어진 순서대로 문자를 삭제할 경우, 문자열 전체를 훑어야 하므로 TLE가 뜬다. 결과 문자열의 뒷부분이 일치하는지를 확인하는 방식으로 시간을 줄일 수 있다.
with open("censor.in", "r") as f:
s = f.readline().strip()
t = f.readline().strip()
r = ''
for i in range(len(s)):
r += s[i]
if len(r) >= len(t) and r[-len(t):] == t:
r = r[:-len(t)]
with open("censor.out", "w") as f:
f.write(r+"\n")
2. Cow
C가 나타나면 C의 개수를 하나 올리고, O가 나타나면 지금까지 센 C의 개수만큼 O의 개수를 올리고,
W가 나타나면 O의 개수만큼 W를 증가시키면 W의 갯수가 총 COW의 개수이다.
with open("cow.in", "r") as f:
n = int(f.readline().strip())
s = f.readline().strip()
c = o = w = 0
for i in range(n):
if s[i] == 'C':
c += 1
elif s[i] == 'O':
o += c
elif s[i] == 'W':
w += o
with open("cow.out", "w") as f:
f.write(str(w)+"\n")
3. Cow Hopscotch
N이 15이하로 매우 작으므로 BFS 이용해서 0, 0에서 도착점까지 도착하는 모든 경로를 세면 된다.
b = []
with open("hopscotch.in", "r") as f:
r, c = map(int, f.readline().strip().split())
for _ in range(r):
b.append(list(f.readline().strip()))
def bfs(y, x):
if y == r-1 and x == c-1:
return 1
ans = 0
for i in range(y+1, r):
for j in range(x+1, c):
if b[i][j] != b[y][x]:
ans += bfs(i, j)
return ans
with open("hopscotch.out", "w") as f:
f.write(str(bfs(0, 0))+'\n')
02 Mar 2020
|
Python
USACO
PS
USACO 2015 January
1. Cow Routing
입력에서 A, B가 순서대로 존재하는지 확인하면서 이동가능한 루트가 존재하면 최소 가격과 비교해서 갱신하면 되는 문제
ans = float('inf')
with open("cowroute.in", "r") as f:
a, b, n = map(int, f.readline().strip().split())
for _ in range(n):
cost, city_num = map(int, f.readline().strip().split())
cities = list(map(int, f.readline().strip().split()))
flag = False
for city in cities:
if city == a:
flag = True
if city == b and flag:
ans = min(ans, cost)
with open("cowroute.out", "w") as f:
if ans == float('inf'):
f.write(str(-1)+'\n')
else:
f.write(str(ans)+'\n')
2. Cow Routing II
전 문제와 동일하나 이번에는 두개의 루트를 선택해서 이동 가능.
단순히 A와 B루트를 덧붙여서 1번 문제와 같은 방식으로 돌리면 TLE가 뜨므로 시간을 줄일 방법을 찾아야 함.
루트에 A나 B가 존재하면 A까지의 루트, A 다음의 루트, B 이전의 루트로 루트를 구분
그 이후 A 다음의 루트와 B 이전의 루트의 교집합이 존재하면 A 에서 B로 이동가능하다는 의미이므로 최소값 계산에 이용하면 됨.
ans = float('inf')
routes = []
costs = []
with open("cowroute.in", "r") as f:
a, b, n = map(int, f.readline().strip().split())
for _ in range(n):
cost, city_num = map(int, f.readline().strip().split())
costs.append(cost)
routes.append(list(map(int, f.readline().strip().split())))
se = [[set(), set(), set()] for _ in range(n)] # till_a, after_a, till_b,
for i in range(n):
s = set()
s2 = set()
flag = False
for city in routes[i]:
s.add(city)
s2.add(city)
if city == a:
se[i][0] = s
s = set()
flag = True
if city == b:
se[i][2] = s2
s2 = set()
if city == b and flag:
ans = min(ans, costs[i])
se[i][1] = s
for i in range(n):
for j in range(n):
if i == j:
continue
if se[i][0] and se[j][2] and (se[i][1] & se[j][2]):
ans = min(ans, costs[i]+costs[j])
with open("cowroute.out", "w") as f:
if ans == float('inf'):
f.write(str(-1)+'\n')
else:
f.write(str(ans)+'\n')
3. It’s All About the Base
뭔가 신박한 알고리즘이 있을지 알고 고민하다가 떠오르는게 없어 정답을 봤는데 전혀 그런거 없다.
단순하게 두 숫자를 10진법부터 변환하고 크기가 더 작은 수에 대해 진법을 하나씩 더 올려 계산하며 같은 숫자를 찾으면 된다. python으로 3초 가까운 시간이 걸리는데도 TLE가 뜨지 않는다.
res = []
with open("whatbase.in", "r") as f:
k = int(f.readline())
for _ in range(k):
x, y = f.readline().strip().split()
X = Y = 10
while X <= 15000 and Y <= 15000:
num_x = int(x[0])*X*X + int(x[1])*X + int(x[2])
num_y = int(y[0])*Y*Y + int(y[1])*Y + int(y[2])
if num_x < num_y:
X += 1
elif num_y < num_x:
Y += 1
else:
res.append([X, Y])
break
with open("whatbase.out", "w") as f:
for r in res:
f.write(str(r[0])+" "+str(r[1])+"\n")
4. Meeting Time
N이 1~16개 이고 단방향으로 가므로 Bessie와 Elsie에 대해 목적지까지 가는 패스를 전부 계산해서 cost를 저장한 후, 두 패스 중 겹치는 최소값을 찾으면 된다. BFS를 이용해서 경우의 수를 찾고, 해당 값을 set에 저장한 후 교집합의 최소값을 출력하는 방식으로 해결했다.
from _collections import deque
b_cost = set()
e_cost = set()
path = dict()
with open("meeting.in", "r") as f:
n, m = map(int, f.readline().strip().split())
for i in range(n):
path[i] = []
for _ in range(m):
a, b, c, d = (map(int, f.readline().strip().split()))
path[a].append([b, c, d])
q = deque([[1, 0]])
while q:
cur, cost = q.popleft()
if cur == n:
b_cost.add(cost)
else:
for p in path[cur]:
q.append([p[0], cost+p[1]])
q = deque([[1, 0]])
while q:
cur, cost = q.popleft()
if cur == n:
e_cost.add(cost)
else:
for p in path[cur]:
q.append([p[0], cost+p[2]])
with open("meeting.out", "w") as f:
if b_cost & e_cost:
f.write(str(min(b_cost & e_cost))+"\n")
else:
f.write("IMPOSSIBLE\n")
25 Feb 2020
|
Python
USACO
PS
USACO 2014 December
1. Marathon
전체 좌표들의 총 맨하탄 거리를 계산한 후 순서대로 한 점씩 뺀 상태로 계산해서 최소값을 찾으면 된다.
즉 A, B, C 가 있을 경우 B를 뺀다고 가정하면 A-B 거리와 B-C 거리를 빼고 A-C 거리를 더하는 방식으로
O(n)으로 훑으면 된다.
pos = []
with open("marathon.in", "r") as f:
n = int(f.readline())
for _ in range(n):
pos.append(list(map(int, f.readline().strip().split())))
res = []
for i in range(1, n):
res.append(abs(pos[i-1][0]-pos[i][0]) + abs(pos[i-1][1]-pos[i][1]))
ans = tot = sum(res)
for i in range(1, n-1):
ans = min(ans, tot - res[i-1] - res[i] + abs(pos[i-1][0]-pos[i+1][0]) + abs(pos[i-1][1]-pos[i+1][1]))
with open("marathon.out", "w") as f:
f.write(str(ans)+"\n")
2. Crosswords
2차원 리스트를 순서대로 돌면서 조건에 맞으면 정답에 추가하는 단순 구현 문제
b = []
with open("crosswords.in", "r") as f:
n, m = map(int, f.readline().strip().split())
for _ in range(n):
b.append(list(f.readline().strip()))
ans = 0
res = []
for i in range(n):
for j in range(m):
if b[i][j] == '#':
continue
hori = vert = True
if i-1 < 0 or (i > 0 and b[i-1][j] == '#'):
for k in range(3):
if i+k >= n or b[i+k][j] != '.':
vert = False
else:
vert = False
if j-1 < 0 or (j > 0 and b[i][j-1] == '#'):
for k in range(3):
if j+k >= m or b[i][j+k] != '.':
hori = False
else:
hori = False
if hori or vert:
ans += 1
res.append([i+1, j+1])
with open("crosswords.out", "w") as f:
f.write(str(ans)+"\n")
for r in res:
f.write(str(r[0])+' '+str(r[1])+'\n')
3. Cow Jog
따라잡히는 속도는 최소값에 의해 좌우되므로 입력을 거꾸로 거슬러 올라가며 최소 속도가 갱신될 때 그룹을 하나씩 추가하면 된다.
a = []
with open("cowjog.in", "r") as f:
n = int(f.readline())
for _ in range(n):
a.append(list(map(int, f.readline().strip().split())))
min_s = 1000000001
ans = 0
for i in range(n-1, -1, -1):
if a[i][1] <= min_s:
ans += 1
min_s = a[i][1]
with open("cowjog.out", "w") as f:
f.write(str(ans)+'\n')
4. Learning by Example
입력된 소의 무게를 정렬하고, 각각의 무게별 구간을 나눈 후 앞의 소가 점이 있으면 앞 절반의 갯수를 ans에 추가하고, 뒤쪽의 소가 spot이 있으면 뒤쪽 절반의 갯수를 ans에 추가한다.
이때 구간은 [S+1, E]의 형태로 잡아서 같은 점이 두번 잡히지 않게 하며,
중앙을 나눌때는 왼쪽 소에 점이 있으면 [S+1, M]까지 추가하고, 오른쪽 소에 점이있으면 [M+1, E]을 추가한다.
이때 오른쪽 소에만 점이 있고, M이 정중앙에 있으면 M의 값이 추가되지 않을 수 있으므로 예외를 처리해줘야 한다.
c = []
with open("learning.in", "r") as f:
n, a, b = map(int, f.readline().strip().split())
for _ in range(n):
s, w = f.readline().strip().split()
w = int(w)
s = 1 if s == 'S' else 0
c.append([w, s])
ans = 0
c.append([-float("inf"), 0])
c.append([float("inf"), 0])
c.sort()
for i in range(n+1):
s = c[i][0]
e = c[i+1][0]
m = (s+e)//2
if c[i][1]:
c_s = max(a, s+1)
c_e = min(b, m)
if c_e >= c_s:
ans += c_e-c_s+1
if c[i+1][1]:
c_s = max(a, m+1)
c_e = min(b, e)
if c_e >= c_s:
ans += c_e-c_s+1
if c[i+1][1] and c[i][1] == 0 and s % 2 == e % 2 and a <= m <= b:
ans += 1
with open("learning.out", "w") as f:
f.write(str(ans)+'\n')
24 Oct 2019
|
Python
Codeforces
PS
Codeforces 라운드 #595 (Div. 3)
요즘 알고리즘 인터뷰 하는 것이 대세인지라 PS를 조금 해봤는데, 생각보다 재미있어서 좀더 깊게 파봐야 겠다는 생각을 했다.
대회에만 나오는 알고리즘까지 공부하고 싶진 않지만 문제풀이 자체는 재미있는지라 꾸준히 참여해보려 한다.
1249A
조건대로 가면 1 그룹 아니면 2 그룹으로 나뉘어진다.
정렬하고 차이 1 나는거 있으면 1 아니면 2 출력해서 끝
1249B1
문제에서 요구하는 그대로 코드로 옮겼다.
tc = int(input())
for _ in range(tc):
n = int(input())
p = list(map(int, input().split()))
for i in range(n):
ans = 1
pos = p[i]
while pos != i+1:
ans += 1
pos = p[pos-1]
print(ans, end=' ')
print()
1249B2
위 문제와 같으나 쿼리가 1000개로 증가, n 의 값도 2*105으로 증가했다.
위의 방법대로 풀면 TLE가 나게 되므로 최적화 할 방법을 생각해야 한다.
예제 입력인 4 6 2 1 5 3
를 살펴보면 첫번째 숫자인 4는 1을 거쳐 다시 4가 되고,
네번째 숫자인 1의 경우엔 4를 거쳤다가 다시 4가 되는 것을 알수 있다.
마찬가지로 두번째 6의 경우는 3 -> 2를 거쳐서 돌아오고, 세번째 2의 경우 6->3을 거쳐서 자신으로 돌아온다.
이는 자신으로 돌아올때까지 만나는 모든 숫자는 같은 횟수를 가진다는 의미이므로 이를 이용하면 O(N)으로 중복 계산을 없앨 수 있다.
tc = int(input())
for _ in range(tc):
n = int(input())
p = list(map(int, input().split()))
res = [0]*n
for i in range(n):
if res[i] == 0:
ans = 1
pos = p[i]
cycle = [i]
while pos != i+1:
cycle.append(pos-1)
ans += 1
pos = p[pos-1]
for c in cycle:
res[c] = ans
print(*res)
1941C1
1941C2
3진법으로 표기했을 때 2가 없으면 그대로 출력하고, 2가 있으면 2가 있는 자리까지는 0으로 바꾸고 자릿수 Carry를 올려주는 방식으로 풀었다.
tc = int(input())
for _ in range(tc):
n = t = int(input())
nums = []
while n:
nums.append(n % 3)
n //= 3
if 2 not in nums:
print(t)
else:
carry = 0
for i in range(len(nums)):
nums[i] += carry
carry = 0
if nums[i] >= 2:
for j in range(i + 1):
nums[j] = 0
carry = 1
if carry:
nums.append(1)
ans = 0
for i in range(len(nums)):
ans += nums[i] * (3**i)
print(ans)
1249D1
인덱스를 앞에서 부터 조사해 나가다가 겹치는 부분이 k보다 크면 가장 긴 세그먼트를 제거해주면 된다.
입력 크기가 크지 않아 O(N3)로 풀어도 된다.
n, k = map(int, input().split())
segs = [0] * n
max_idx = 210
cnt = [0] * max_idx
for i in range(n):
l, r = map(int, input().split())
segs[i] = (l, r)
cnt[l] += 1
cnt[r+1] -= 1
for i in range(max_idx-1):
cnt[i+1] += cnt[i]
ans = [0] * n
for i in range(max_idx):
while cnt[i] > k:
pos = -1
for p in range(n):
if not ans[p] and segs[p][0] <= i <= segs[p][1] and (pos == -1 or segs[p][1] > segs[pos][1]):
pos = p
for j in range(segs[pos][0], segs[pos][1]+1):
cnt[j] -= 1
ans[pos] = 1
print(sum(ans[0:n]))
for i, v in enumerate(ans):
if v:
print(i+1, end=' ')
입력 받으면서 세그먼트의 왼쪽 끝 부분은 +1, 오른쪽 끝 다음 칸을 -1 한 후 인덱스 순서대로 숫자를 누적시키면 세그먼트의 누적합을 만들 수 있다.
1 0 0 3 3 5 2 2 4 0 0 1
과 같은 형태가 되는데 이때 하나씩 탐색하며 k보다 많이 겹친 부분을 찾으면 모든 세그먼트를 탐색하며 k에 걸친 세그먼트 인지, 이미 삭제된 세그먼트 인지 (ans 리스트에 체크) 확인 후 가장 긴 세그먼트를 pos에 넣고 그 세그먼트의 누적합을 제거한다.
1249D2
위쪽 문제와 동일하나 입력 크기가 크다. 세그먼트의 왼쪽 인덱스를 기준으로 오른쪽 인덱스가 가장 긴 순서로 쌓아놓고,
인덱스의 처음부터 순회하며 세그먼트가 있을때마다 추가하고, 만약 세그먼트의 중복 크기가 k를 넘으면 가장 오른쪽으로 긴 세그먼트를 제거한다.
가장 긴 세그먼트를 제거해야하므로 추가 후 순서가 정렬되어 있어야 한다.
이 부분이 가장 시간소모가 크므로 heap을 이용해서 삽입과 동시에 정렬이 최적화 되도록 한다.
import sys
from heapq import *
n, k = map(int, input().split())
N = int(2e5 + 2)
ll = [[] for _ in range(N)]
rr = [[] for _ in range(N)]
vis = [0] * N
for i in range(n):
l, r = map(int, sys.stdin.readline().split())
ll[l].append((-r, i + 1))
rr[r].append(i + 1)
q = []
ans = []
size = 0
for i in range(1, N):
for j in ll[i]:
heappush(q, j)
size += 1
while size > k:
cur = heappop(q)
while vis[cur[1]]:
cur = heappop(q)
vis[cur[1]] = 1
ans.append(cur[1])
size -= 1
for j in rr[i]:
if not vis[j]:
vis[j] = 1
size -= 1
print(len(ans))
print(*ans)