algorithm/problem solving
leetcode 39(backtracking), 1353(그리디), 128(two pointer), 368(lis)
qkqhxla1
2021. 12. 22. 21:31
39 https://leetcode.com/problems/combination-sum/
별다른 설명이 필요 없다. 백트래킹 연습용으로 좋은 문제다.
class Solution: def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: #candidates = [1,2] #target = 4 ret = [] def get_comb(candidates, cand, target): if target == 0: ret.append(cand) return for i in range(len(candidates)): if target-candidates[i] >=0: get_comb(candidates[i:], cand + [candidates[i]], target-candidates[i]) get_comb(candidates, [], target) return ret
1353 https://leetcode.com/problems/maximum-number-of-events-that-can-be-attended/
모든 이벤트들을 시작일 기준으로, 그다음엔 끝나는 날 기준으로 정렬한 후에 시작일을 하나하나 돌아가면서 같은 시작일일 경우 그 이벤트들의 끝나는 날짜를 최소 힙에 저장해둔다. 그다음 최소 힙에서 pop하면 '같은 시작일이면서 빨리 끝나는 이벤트' 순으로 pop이 될것이므로 이것을 이용해서 최대한 많은 이벤트를 참석할수 있도록 조율한다.
from heapq import * class Solution: def maxEvents(self, events: List[List[int]]) -> int: #events= [[1,2],[1,2],[1,6],[1,2],[1,2]] events.sort(key=lambda x:(x[0], x[1])) # 이벤트들을 시작일 기준으로, 그다음엔 끝나는 일 기준으로 정렬한다. end_day = max([e[1] for e in events]) # 모든게 끝나는 날짜 heap = [] ret = 0 cur_day = -1 i = 0 while cur_day <= end_day: if cur_day == -1: # cur_day를 초기화해준다. cur_day = events[i][0] # cur_day는 현재 존재하는 가장 빨리 시작하는 이벤트의 시작일. while i < len(events) and events[i][0] == cur_day: # 시작일이 같은 이벤트들을 먼저 처리하기 위해 힙에 저장해둔다. heappush(heap, events[i][1]) # 시작일이 같은 이벤트들의 끝나는 날짜만 힙에 저장해둔다. 빨리 끝나는거부터 처리하기 위함 i += 1 # [[1,2],[1,2],[1,6],[1,2],[1,2]] 같은 예외케이스를 처리하기 위함. # 이벤트의 끝나는 날이 이미 지나갔으면(cur_day보다 작으면) 참석 못하는거니 그냥 버린다. while heap and heap[0] < cur_day: heappop(heap) if heap: # 참석할 이벤트가 있으면 참석한다. ret += 1 heappop(heap) cur_day += 1 return ret
128 https://leetcode.com/problems/longest-consecutive-sequence/
[-1, -1, 0, 1, 3, 4, 5, 6, 7, 8, 9] 와 같은 리스트가 있다고 할 경우 증가하는 부분집합들은 (-1, 0, 1), (3,4,5,6,7,8,9) 가 있다. 이것들을 찾으려면 two pointer로 앞점과 뒷점을 만든 후, 앞점 + 1 == 뒷점인 경우 한칸씩 뒤로 증가시켜주면서 길이를 늘려주되, 앞점 + 1 != 뒷점이면 길이를 다시 1로 초기화해주면 된다.
class Solution: def longestConsecutive(self, nums: List[int]) -> int: #nums = [9,1,4,7,3,-1,0,5,8,-1,6] if not nums: return 0 nums.sort() # 오름차순 정렬 ret = -1 i = 0 while i < len(nums): # 모든 경우에 대해서 계산 _len = 1 _prev, _next = i, i+1 # _prev는 two pointer의 앞점, _next는 뒷점이 될 예정 while _next < len(nums): while _next < len(nums)-1 and nums[_prev] == nums[_next]: # lcs의 뒷점이 앞점과 같으면 다른곳까지 가라고 늘려준다. _next += 1 if nums[_prev] + 1 == nums[_next]: # 앞점과 뒷점이 1 차이이면 길이를 1 늘려준다. _len += 1 else: # 앞점과 뒷점의 차이가 1 초과면 길이를 초기화해준다. _len = 1 ret = max(ret, _len) _prev = _next # 앞점을 이전 뒷점으로, _next += 1 # 뒷점을 1 증가신후 다시 판별 i = _prev + 1 return ret
368 https://leetcode.com/problems/largest-divisible-subset/
O(n^2) lis와 같은 방법론으로 풀수 있다.
class Solution: def largestDivisibleSubset(self, nums: List[int]) -> List[int]: #nums = [5,9,18,54,108,540,90,180,360,720] nums.sort() ret = [[n] for n in nums] for i in range(len(nums)): for j in range(i): if nums[i] % nums[j] == 0 and len(ret[i]) < len(ret[j])+1: ret[i] = ret[j] + [nums[i]] return sorted(ret, key=lambda x: -len(x))[0]
300번이 lis문제인데 https://leetcode.com/problems/longest-increasing-subsequence/ 이것을 O(n^2)으로 풀면 아래와 같다.
class Solution: def lengthOfLIS(self, nums: List[int]) -> int: #nums = [10,9,2,5,3,7,101,18] ret = [1]*len(nums) for i in range(len(nums)): for j in range(i): if nums[i] > nums[j] and ret[i] < ret[j] + 1: ret[i] = ret[j] + 1 return max(ret)