TIl

부분집합, 조합, 그리디 심화

아크몽 2024. 2. 28. 23:22

부분집합

예시) 집합에 포함된 원소들을 선택하는 것이다.

부분집합에는 공집합(아무것도 선택하지 않은 경우)도 집합에 포함됨

집합에서 부분 집합을 찾아내는 구현 방법

  1. 완전 탐색 (학습용으로만 추천)
  2. Binary Counting (비트연산) (실전용)

바이너리 방식 예시) 친구 5명중 최소 2명이상으로 구성될 경우

friends = ['a','b','c','d','e']
n = 5 # 친구의 수
m = 2 # 2명이상
# 5P2
def getFriend(tar):   cnt = 0   for i in range(n):     # 1비트 1인지 확인     if tar & 0x1:       # print(friends[i], end='')       cnt += 1     # right shift 비트 연산자 -> 오른쪽 끝 비트를 하나씩 제거     tar >>= 1   return cnt
# getFriend(m)
result = 0
for j in range(1<= m: # bit가 2개이상 1이라면 -> 2명 이상이라면?     result += 1
print(result)
# 같은 결과를 도출함
n = 3
# 첫 번째 방법
for i in range(1<>= 1   print()

조합 Combination

서로 다른 n개의 원소 중 r개를 순서 없이 골라 낸 것

friends = ['a','b','c','d','e']
m = 5 # 친구의 수
n = 3

# 반복문 방법
for a in range(5):
  start1 = a+1
  for b in range(start1,5):
    start2 = b + 1
    for c in range(start2,5):  print(arr[a], arr[b], arr[c])

# 재귀 방법
path = []
# 5C2
def getFriend(lev, start):   if lev ==n:     print(path)     return    for i in range(start,5):     path.append(friends[i])     getFriend(lev + 1, i+1)     path.pop()

getFriend(0,0)

예제) 주사위 3개 던지기 조합 (중복 조합)

n = 3
path = []
def qube(lev,start):   if lev == n:     print(path)     return    for i in range(start,7):     path.append(i)     qube(lev+1,i)     path.pop()

qube(0,1)

주의할 점 : [A,A] 은 가능하지만 [A,B] 와 [B,A] 는 같이 있을 수 없다. = 조합이니까

Greedy

결정이 필요할 때, 현재 기준으로 가장 좋아 보이는 선택지를 결정해서 값을 도축하는 알고리즘

대표적인 방법

  1. 완전탐색 : 모든 경우의 수
  2. Greedy : 가장 좋아보이는 수
  3. DP : 과거의 데이터를 이용해서 현재의 데이터를 만들어 내는 것
  4. 분할정복

예제) 화장실 문제

arr = [15,30,50,10]
arr.sort()
sum = 0
for i in range(len(arr)):   sum +=arr[i] * (len(arr)-1-i)

print(sum)

예제2)

0-1 knapsack ( 고체류 물건)

도둑이 최대 30kg까지 짐을 담아갈 수 있을 때,

물건의 개수 n, 물건 별 무게(w), 가격(p)

⇒ 1kg당 가격으로 접근하기 ⇒ 그리디로 못푼다. 부분집합으로 풀어야 함

fractional knapsack 만약 물건을 원하는 만큼 자를 수 있을 때 (액체류 물건)

⇒ 그리디 성립해서 풀이 가능

n = 3
target = 30
things = [(5, 50), (10, 60), (20, 140)]
things.sort(key=lambda x: (x[1] / x[0]), reverse=True)

sum = 0
for kg, price in things:   per_price = price / kg    # 만약 가방에 남은 용량이 얼마되지 않는다면?   # 물건을 잘라 가방에 넣고 끝낸다.   if target < kg:     sum += target * per_price     break    sum += price   target -= kg

print(int(sum))

예제 3) 활동선택 문제

(1,4),(3,5),(1,6) ... (12,14)

회의 종료시간이 가장 빠른 회의를 먼저 선택하면 된다.

종료시간을 기준으로 회의들을 오름차순 정렬한다.

종료시간이 가장 빠른 회의를 찾자마자 확정 (1,4)

⇒ 이제 4시 이후로 부터 회의가 가능하다.

4시 이후에 가장 빨리 끝나는 회의 찾기 (5,7)

⇒ 이제 7시 이후로 회의가 가능

7 시 이후로 가장 빨리 끝나는 회의 찾기 (8,11)

⇒ 이제 11시 이후로 회의가능

11시 이후로 가장 빨리 끝나는 회의 (12,14)

⇒ 정답은 4회

문제 전략

  1. 끝나는 시간을 기준으로 오름차순 정렬
  2. 빠르게 끝나는 회의를 선택하여 확정한다.
  3. 이후로 가능한 회의 중, 빠르게 끝나는 회의를 선택하여 확정한다.