LIS 알고리즘

  • 최장 증가 부분 수열
  • 일반적으로 DP(동적 계획법)으로 푸는 알고리즘
  • 임의의 수열이 주어질 때, 이 숭려에서 몇 개의 수들을 제거해서 부분 수열을 만든다.
  • 이 때 부분수열들을 오름차순으로 정렬된 가장 긴 수열을 최장 증가 부분 수열이라고 한다.

 

예시 코드

  • 아래의 배열이 주어졌을 때
arr = [3,5,7,9,2,1,4,8]

 

  • 아래처럼 부분 수열을 만들 수 있다.
  • 이 중에 3,5,7,8이 LIS
arr = [3,7,9,1,4,8]
arr = [7,9,1,8]
arr = [3,5,7,8]
arr = [1,4,8]

 

일반적인 DP 풀이 방법 - O(n^2)

dp = [1]*n
for i in range(n):
	for j in range(i):
    	if arr[i] > arr[j]:
    		dp[i] = max(dp[i], dp[j]+1)

 

이분 탐색 이용 - O(nlogn)

# 수열 A가 주어질 때, 가장 긴 증가하는 부분 수열 구하기
n = 6
arr = [10,20,10,30,20,50]
lis = [0]

for case in arr:
    if lis[-1] < case:
        lis.append(case)
    else:
        left = 0
        right = len(lis)

        while left < right:
            mid = (left+right)//2
            if lis[mid] < case:
                left = mid + 1
            else:
                right = mid
        lis[right] = case
print(lis[1:])

 

 

백준 풀이 코드

https://hhiyeon.tistory.com/168

 

[백준] 11053번 가장 긴 증가하는 부분수열 - dp, LIS

https://www.acmicpc.net/problem/11053 11053번: 가장 긴 증가하는 부분 수열 수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20,..

hhiyeon.tistory.com

 

https://hhiyeon.tistory.com/166

 

[백준] 12015번 가장 긴 증가하는 부분수열2 - 이분탐색, LIS

https://www.acmicpc.net/problem/12015 12015번: 가장 긴 증가하는 부분 수열 2 첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1..

hhiyeon.tistory.com

 

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

 

12015번: 가장 긴 증가하는 부분 수열 2

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000,000)

www.acmicpc.net

 

LIS 알고리즘 정리

https://hhiyeon.tistory.com/167

 

[알고리즘] LIS(Longest Increasing Subsequence) 최장 증가 부분 수열

LIS 알고리즘 최장 증가 부분 수열 일반적으로 DP(동적 계획법)으로 푸는 알고리즘 임의의 수열이 주어질 때, 이 숭려에서 몇 개의 수들을 제거해서 부분 수열을 만든다. 이 때 부분수열들을 오름

hhiyeon.tistory.com

 

백준 풀이

#n = int(input())
#arr = list(map(int, input().split()))
n = 6
arr = [10, 20, 10, 30, 20, 50]
lis = [0]

for case in arr:
    if lis[-1] < case:
        lis.append(case)
    else:
        left = 0
        right = len(lis)

        while left < right:
            # print(lis)
            mid = (left+right)//2
            if lis[mid] < case:
                left = mid + 1
            else:
                right = mid
        lis[right] = case
print(len(lis)-1)

 

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

 

10815번: 숫자 카드

첫째 줄에 상근이가 가지고 있는 숫자 카드의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 숫자 카드에 적혀있는 정수가 주어진다. 숫자 카드에 적혀있는 수는 -10,000,000보다 크거나 같고, 10,

www.acmicpc.net

 

import sys
n = int(input())
cNum = list(map(int, sys.stdin.readline().split()))
m = int(input())
card = list(map(int, sys.stdin.readline().split()))
cNum = sorted(cNum)
answer = ''

for num in card:
    lt = 0
    rt = n - 1
    flag = 0
    while lt <= rt:
        mid = (lt + rt) // 2
        if cNum[mid] < num:
            lt = mid +1
        elif cNum[mid] == num:
            flag = 1
            break
        else:
            # cNum[lt] > num
            rt = mid -1
    answer += str(flag) + ' '
print(answer[:-1])

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

 

14888번: 연산자 끼워넣기

첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수, 

www.acmicpc.net

 

n = 3
op = [3,4,5] # 주어진 수열 - 순서 바꾸기 X
add, sub, mul, div = 1, 0, 1, 0  # 덧셈 뺄셈 곱셈 나눗셈 개수
#n = int(input())
#op = list(map(int, input().split()))
#add, sub, mul, div = map(int, input().split())
minValue = float('inf')
maxValue = float('-inf')

def dfs(i, arr):
    global minValue, maxValue, add, sub, mul, div
    if i == n:
        # 최대값 최소값 구하기
        maxValue = max(maxValue, arr)
        minValue = min(minValue, arr)
    else:
        if add > 0:
            add -= 1
            dfs(i+1, arr+op[i])
            add += 1
        if sub > 0:
            sub -= 1
            dfs(i+1, arr-op[i])
            sub += 1
        if mul > 0:
            mul -= 1
            dfs(i+1, arr*op[i])
            mul += 1
        if div > 0:
            div -= 1
            dfs(i+1, int(arr/op[i]))
            div += 1

dfs(1, op[0])
print(maxValue)
print(minValue)

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

 

14889번: 스타트와 링크

예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.

www.acmicpc.net

 

n = int(input())
arr = [list(map(int, input().split())) for _ in range(n)]
minValue = float('inf')
t1 = [] # 스타트팀
# t2 = [] # 링크팀

# 스타트팀 - 링크팀 능력치 최소값 출력
# (1,3,6) (2,4,5) -> 13,31,16,61,36,63
def calculator(combList):
    total = 0
    for x in range(len(combList)):
        a = combList[x][0]-1
        b = combList[x][1]-1
        total += (arr[a][b] + arr[b][a])
    return total

def dfs(start):
    global minValue
    if len(t1) == n//2:
        t2 = []
        diff = 0
        for i in range(1, n+1):
            if i not in t1:
                t2.append(i)
        combList1 = list(comb(t1, 2))
        combList2 = list(comb(t2, 2))
        total1 = calculator(combList1)
        total2 = calculator(combList2)
        diff = abs(total1 - total2)

        if minValue > diff:
            minValue = diff
    else:
        for idx in range(start, n+1):
            if idx not in t1:
                t1.append(idx)
                dfs(idx)
                t1.pop()
dfs(1)
print(minValue)

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

 

2798번: 블랙잭

첫째 줄에 카드의 개수 N(3 ≤ N ≤ 100)과 M(10 ≤ M ≤ 300,000)이 주어진다. 둘째 줄에는 카드에 쓰여 있는 수가 주어지며, 이 값은 100,000을 넘지 않는 양의 정수이다. 합이 M을 넘지 않는 카드 3장

www.acmicpc.net

 

n, m = map(int, input().split())
arr = list(map(int, input().split()))
maxValue = 0
stack = []

def dfs():
    global maxValue
    if len(stack) == 3: # 3장의 카드
        total = 0
        for value in stack: # 카드의 합 구하기
            total += arr[value]
        if maxValue < total and total <= m: # m의 수를 넘지 않으면서, 최대값 구하기
            maxValue = total
    else:
        for idx in range(n):
            if idx not in stack:
                stack.append(idx)
                dfs()
                stack.pop()

dfs()
print(maxValue)

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

 

10819번: 차이를 최대로

첫째 줄에 N (3 ≤ N ≤ 8)이 주어진다. 둘째 줄에는 배열 A에 들어있는 정수가 주어진다. 배열에 들어있는 정수는 -100보다 크거나 같고, 100보다 작거나 같다.

www.acmicpc.net

 

n = int(input())
arr = list(map(int, input().split()))
s = []
maxValue = 0

def dfs():
    global maxValue
    if len(s) == n: # 리스트에 저장된 정수의 개수가 n 일때
        total = 0
        for x in range(1, n): # 최대값을 구해준다
            total += abs(arr[s[x-1]] - arr[s[x]])
        maxValue = max(maxValue, total)
    else:
        for idx in range(n): # idx 0부터 ~ n까지 (값 중복 가능) 
            if idx not in s:
                s.append(idx)
                dfs()
                s.pop()

dfs()
print(maxValue)

완전탐색

- 모든 경우의 수를 계산하는 방법

 

백트래킹

- 주어진 문제에서 답을 구하기 위해 가능한 경우의 수를 탐색하는 방법

- 해를 찾는 도중에 찾는 해가 아닌 경우 되돌아가서 해를 찾는다.

 

백트래킹 문제 

1. N과 M(1) 15649번

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

 

15649번: N과 M (1)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

 

n, m = 4, 2
#n, m = map(int, input().split())
s = [] # 수열을 저장하는 리스트

def dfs():
    if len(s) == m: # 리스트의 길이가 m개인 경우
        print(' '.join(map(str, s))) # 리스트의 모든 숫자들을 출력
        return 
    for i in range(1, n+1): # 1부터 n+1
        if i not in s: # 리스트 안에 i(1~4) 가 없으면 
            s.append(i) # 리스트에 i(1~4) 추가
            dfs() # 다음 레벨
            s.pop() # 마지막으로 넣었던 i 제거
dfs()



n, m = 4,2
stack = []

def dfs(start):
    if len(stack) == m:
        # print(stack)
        print(' '.join(map(str, stack)))
        return 
    for x in range(1, n+1):
        if x not in stack:
            stack.append(x)
            dfs(start+1)
            stack.pop()
dfs(1)

 


3. N과 M(3) 15650번

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

 

15650번: N과 M (2)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

 

n, m = map(int, input().split())
stack = []

def dfs(start):
    if len(stack) == m:
        print(' '.join(map(str, stack)))
        return
    else:
        for x in range(start, n+1):
            if x not in stack:
                stack.append(x)
                #print(stack)
                dfs(x+1)
                stack.pop()
dfs(1)

 


3. N과 M(3) 15651번

 

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

 

15651번: N과 M (3)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

 

# n, m = map(int, input().split())
n, m = 4,2 
stack = []

def dfs():
    if m == len(stack):
        print(' '.join(map(str, stack)))
        return
    for x in range(1, n+1):
        stack.append(x)
        dfs()
        stack.pop()
dfs()

 


4. N과 M(4) 15652번

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

 

15652번: N과 M (4)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

 

#n, m = map(int, input().split())
s = [] 

def dfs(start):
    if len(s) == m: # 리스트의 길이가 m개인 경우
        print(' '.join(map(str, s))) # 리스트의 모든 숫자들을 출력
        return 
    for i in range(start, n+1): # start으로 1,1 2,2, 3,3
        s.append(i) # 리스트에 i(1~4) 추가
        dfs(i) # 다음 레벨
        s.pop() # 마지막으로 넣었던 i 제거
dfs(1)

 


5. N과 M(5) 15654번

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

 

15654번: N과 M (5)

N개의 자연수와 자연수 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오. N개의 자연수는 모두 다른 수이다. N개의 자연수 중에서 M개를 고른 수열

www.acmicpc.net

 

n, m = map(int, input().split())
# n, m = 4, 2 # n개의 수를 입력받고, m개의 중복없는 수열을 구하기
# arr = [9,8,7,1]
arr = list(map(int, input().split()))
arr = sorted(arr) # 사전순 출력
stack = []

def dfs():
    if len(stack) == m:
        for idx in stack:
            print(arr[idx], end=' ')
        print()
        return
    for x in range(0, n):
        if x not in stack:
            stack.append(x)
            dfs()
            stack.pop()
dfs()

 


6. N과 M(6) 15655번

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

 

15655번: N과 M (6)

N개의 자연수와 자연수 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오. N개의 자연수는 모두 다른 수이다. N개의 자연수 중에서 M개를 고른 수열

www.acmicpc.net

 

#n, m = map(int, input().split())
#arr = list(map(int, input().split()))
n, m = 4, 4
arr = [1231, 1232, 1233, 1234]
arr = sorted(arr)
stack = []

def dfs(start):
    if len(stack) == m:
        for idx in stack:
            print(arr[idx], end=' ')
        print()
        return
    for x in range(start, n):
        if x not in stack:
            stack.append(x)
            dfs(x+1)
            stack.pop()
dfs(0)

 

 


7. N과 M(7) 15656번

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

 

15656번: N과 M (7)

N개의 자연수와 자연수 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오. N개의 자연수는 모두 다른 수이다. N개의 자연수 중에서 M개를 고른 수열

www.acmicpc.net

 

n, m = map(int, input().split())
arr = list(map(int, input().split()))
#n, m = 4, 2
#arr = [9,8,7,1]
arr = sorted(arr) # 사전순 출력
stack = []

def dfs(start):
    if len(stack) == m:
        for idx in stack:
            print(arr[idx], end=' ')
        print()
        return

    for x in range(0, n):
        # 중복 상관 X
        stack.append(x)
        dfs(start+1)
        stack.pop()

dfs(0)

 


8. N과 M(8) 15657번

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

 

15657번: N과 M (8)

N개의 자연수와 자연수 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오. N개의 자연수는 모두 다른 수이다. N개의 자연수 중에서 M개를 고른 수열

www.acmicpc.net

 

#n, m = map(int, input().split())
#arr = list(map(int, input().split()))

n, m = 4, 2
arr = [9,8,7,1]
arr = sorted(arr)
stack = []

def dfs(start):
    if len(stack) == m:
        for idx in stack:
            print(arr[idx], end=' ')
        print()
        return
    
    for x in range(start, n):
        stack.append(x)
        dfs(x)
        stack.pop()
dfs(0)

 


9. N과 M(9) 

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

 

15663번: N과 M (9)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

 

#n, m = map(int, input().split())
#arr = list(map(int, input().split()))

n, m = 4, 2
arr = [9,7,9,1]
arr = sorted(arr)
visited = [False]*n # 사용한 원소 체크
stack = []
answer = []

def dfs(v):
    if len(stack) == m:
        answer.append(tuple(stack))
        return
    
    for x in range(0, n):
        if visited[x]:
            continue
        stack.append(arr[x])
        visited[x] = True # 사용
        dfs(v+1)
        visited[x] = False # 사용끝
        stack.pop() 
dfs(0)

# 오름차순, set 중복제거
for x in sorted(list(set(answer))):
    print(*x) # 튜플 출력

 


10. N과 M(10) 15664번

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

 

15664번: N과 M (10)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

 

n, m = map(int, input().split())
arr = list(map(int, input().split()))
#n, m = 4,2
#arr = [999,1,23,5]
#arr = [9,7,9,1]
arr = sorted(arr) # 오름차순
stack = []

def dfs(v):
    if len(stack) == m:
        print(stack)
        return

    prev = 0
    for x in range(v, n):
        if prev != arr[x]:
            stack.append(arr[x])
            prev = arr[x]
            dfs(x+1)
            stack.pop()
dfs(0)

 


11. N과 M(11) 15665번

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

 

15665번: N과 M (11)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

 

n, m = map(int, input().split())
arr = list(map(int, input().split()))
arr = sorted(arr)
stack = []

def dfs():
    if len(stack) == m:
        print(*stack)
        return
    prev = 0
    for x in range(0, n):
        if prev != arr[x]:
            stack.append(arr[x])
            prev = arr[x]
            dfs()
            stack.pop()
dfs()

12. N과 M(12) 15666번

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

 

15666번: N과 M (12)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

 

#n, m = map(int, input().split())
#arr = list(map(int, input().split()))
n, m = 4, 2
arr = [9,7,9,1]
arr = sorted(arr)
stack =[]

def dfs(v):
    if len(stack) == m:
        print(*stack)
        return
    prev = 0
    for x in range(v, n):
        if prev != arr[x]:
            stack.append(arr[x])
            prev = arr[x]
            dfs(x)
            stack.pop()
dfs(0)

 

6603번 로또

def dfs(stack, numbers, start, k):
    if len(stack) == 6:
        print(' '.join(map(str, stack)))
        return
    for x in range(start, len(numbers)):
        dfs(stack + [numbers[x]], numbers, x + 1, k)


while True:
    num = list(map(int, input().split()))
    if num[0] == 0:
        break

    k = num[0]  # 번호 개수
    numbers = sorted(num[1:])  # 번호 리스트

    visited = [False] * len(numbers)
    dfs([], numbers, 0, k)
    print()

 

1182번 부분수열의합

n, s = map(int, input().split())  # 정수의 개수, 정수 S
numbers = list(map(int, input().split()))

# n, s = 5, 0
# numbers = [-7, -3, -2, 5, 8]
cnt = 0


# 크기가 양수인 부분 수열 중에서 다 더한 값이 S인 경우의 수 구하기

def dfs(idx, cost):
    global cnt
    if idx >= n:  # 인덱스가 정수 개수보다 크면 종료
        return
    cost += numbers[idx]  # 더한값 += 현재 값
    if cost == s:  # 더한 값이 s면 경우의수 + 1
        cnt += 1
    dfs(idx + 1, cost)  # 현재 원소를 포함하는 경우
    dfs(idx + 1, cost - numbers[idx])  # 현재 원소를 포함하지 않는 경우


dfs(0, 0)
print(cnt)

 

9663번 N-Queen

def adjacent(x):
    for i in range(x):
        if row[x] == row[i] or abs(row[x] - row[i]) == x - i:
            return False
    return True


def dfs(x):
    global result

    if x == n:
        result += 1
        return
    for i in range(n):
        if visite[i]:
            continue
        row[x] = i
        if adjacent(x):
            visite[i] = True
            dfs(x + 1)
            visite[i] = False

import sys

# n = int(sys.stdin.readline())
n = 8  # n x n 크기의 체스판
row = [0] * n
visite = [False] * n
result = 0
dfs(0)
print(result)

+ Recent posts