Knapsack 은 Non-Polynomial 한 문제로 (왜? 가짓수만 해도 2^n이기 때문이다.) 어려운 문제에 속한다. 대표적인 다른 Non-Polynomial 로는 Subset이 있다.

Knapsack 의 문제 두 가지 유형을 생각해봤다.
항상 답 자체는 2번 >= 1번이다. (잘라서 넣을 수 있으므로!)
DP의 정의를 다음과 같이 해보자. (용량은 K, 각 아이템의 크기는 S, 이익은 P 배열에 저장된다.)
DP[i][K] 를 아이템 1부터 i까지 중 아이템을 선택하여 용량 K인 가방에 넣을 수 있는 최대 이익이라 해보자. 그러면 구하는 것은 아이템 n개를 가지고 용량이 K인 가방에 넣을 때 최대 이익이므로 DP[n][K]가 된다. (어라? 이번에도 K가 이상하다. Psuedo Poly!)

DP[i][K] = max(DP[i-1]K-S[i] + P[i], DP[i-1][K])
수행시간은 O(nK) 이지만 K는 입력의 크기가 아닌 입력으로 주어지는 수이므로 이것 역시 Pseudo Polynomial time 이다.
<DP 인덱스만 다르게 주고 같은 문제 풀기>
DP[i][p] 아이템 1-i로 가격 p를 맞췄을 때, 해당 가격을 맞추면서 선택한 아이템의 크기 합이 최소 값이라 정의. 크기 합이 정확히 K일 때를 봐야하므로 DP[n][] = K인 가장 높은 p를 찾으면 된다. (가방을 가장 덜 차지하는)
DP[i][p] = min(DP[i-1][p], DP[i-1]p-p[i]] + S[i])
i번째 아이템은 뽑지 않은 경우 나머지에서 크기를 보고, 뽑은 경우 i번째 크기를 더하고 남은 가격을 빼준 상태에서 크기를 본다. 둘 중 작은 것!
DP[n][P], DP[n][P-1] ... 중 첫 번째로 K이하가 될 때 이익이 정답이다. (아이템 n은 모두 탐색해봐야 하고, 그 중 가방 크기에 맞으면서(K) 이익이 최대가 될 경우를 구하므로)
Tree 작성은 시험에 출제될 수 있으니 잘 알아두자.
가방 크기 K = 5
가격 P = [15, 16, 6]
크기 S = [3, 4, 2] 가 주어지면 가성비를 계산할 수 있다.
가성비 = [5, 4, 3]
Backtracking 으로 풀기위해선 기본적으로 준비해야 할 것이 또 있다. 가장 큰 예상 가격의 합을 갱신하는 MP 배열과, 해당 아이템을 담을지 0/1 결정하는 X 배열.
계산은 현재까지의 가격 합 P와 크기 합 S를 가지고 해나가면 된다.
이제 아이템 15, 16, 6짜리를 어떻게 담을지 결정하면 되는데 이때는 판단을 먼저 해야한다. 판단 조건으로는 2가지를 고려한다.
이제 첫 아이템 X[1] = 1 을 고려해보자. (가격 15에, 크기 3)
따라서 P = 15, S = 3인 가방이 완성되었다. 다음 아이템 x[2] = 1을 고려해보자. 가격 16, 크기 4짜리 아이템이다.
이렇게 되면 더이상 내려가지 않고 x[2] = 0 담지 않는 길을 고려한다. 이럴 경우
따라서 현재까지 x = [1, 0] 이며, P = 15, S = 3이다.
x[3] = 1을 고려해보자. 세번째 아이템은 가격 6, 크기 2짜리이다.
다시 올라가 이번엔 x[3] = 0을 고려한다.
계속 올라가 이번엔 x[1] = 0을 고려했으나
말로 하니 어려우나 사실 직접 해보면 더 쉽다. 직접 해보자!

중요한 조건 2를 다시 보자.
조건 2는 그 길로 내려갔을 때 얻을 수 있는 최대 이익 VS 현재까지 best 이익을 비교하는 것이고, 최대 이익이 현재까지 이익보다도 작다면 내려가지 않는 것이다.
코드를 보자.

그래프 알고리즘의 기본 사항들을 알아보자.
표현 방법은


인접행렬과 인접 리스트의 시간 복잡도를 비교해보자.
1. (u,v) 연결 존재?
인접 행렬의 경우 O(1), 인접리스트의 u의 모든 degree를 조사해야 하므로 O(deg(u))
2. u에 인접한 모든 노드?
모든 노드를 알기 위해선 인접 행렬의 경우 행에 접근한다음 그 줄을 다 읽는다. O(n). 인접리스트의 경우 degree 수만큼 스캔하므로 O(deg(u))
3. u,v 삽입?
인접행렬의 경우 해당 칸에 쓰는 연산 O(1). 인접리스트 또한 append 한 번. O(1)
4. 기존 u,v 삭제?
인접행렬의 경우 [u][v] = 0 으로 O(1). 인접리스트의 경우 삭제하면 리스트 나머지 degree 들이 한 칸씩 앞으로 와야하므로 O(deg(u))
5. 메모리?
인접행렬의 경우 O(n^2). 인접 리스트의 경우 O(n + m). 이때 n은 노드 수, m은 엣지 수. 결국 기록하는 [1, 4] 와 같은 것들이 엣지이기 때문. 엣지 수는 방향 그래프일 경우 최대 n(n-1), 무방향일 경우 n(n-1)/2
그래프 순회의 경우 주어진 그래프가 있을 때 inorder, preorder, postorder 의 각 경우를 보는 것이다.
방법으로는 DFS(Depth First Search) 와 BFS(Beadth First Search) 가 있다. DFS 의 경우 preorder 와 같다. BFS는 0층을 다 돌고, 1층을 돌고, 2층을 도는 순서이다.

다음과 같은 그래프를 DFS 로 순회하면서 각각 preorder 와 post order 을 기록해볼 수 있다. 기본적으로 경로가 있는 방향으로 진행하면서 preorder 의 숫자를 늘려가며 기록하면 되는데, 갈 길이 방문했던 곳밖에 없는 경우 해당 위치에 post 를 기록해주면 된다.
DFS order 로 기록한 위 그래프의 순회는 a b d c e h f g 이다. (pre 만 쓰면 된다.)
이 방문순서를 위 그래프에 다시 표기한 후 트리를 그려볼 수 있다. (그래프 > 트리 전환의 순간)

트리에서 발견할 수 있는 edge 에는 4가지가 있다. 하나씩 알아보자.
0. Tree edge: 트리와 그래프 모두에서 나타나는 엣지를 말하며, 위 그래프에서 빨간색으로 표시한 부분을 말한다. (a,b) (b,d) (d,c) (d,e) (d,f), (e,h), (f,h) 7개
이제 주목해야 할 건 이를 제외한 나머지 엣지들이다. 그래프에만 있고 트리에는 없는 엣지들!
forward edge: 트리에서 조상 => 자손 엣지, 그래프에만 있고 트리에는 없는 엣지
(a,c) (b, f)
backward edge: 그래프만 있고 트리는 없는 조상 => 자손 엣지
(c,b)
cross edge: 그래프만 있고 트리는 없는 엣지 중 조상 자손 관계가 아닌 엣지
(g,e) (f,e) (e,c) (g,h)
= 총 14개로 모든 엣지가 완성되었다.
이걸 왜 하냐? 사이클 판단을 쉽게 하기 위해서다. 사이클은 backward edge 1개당 1개씩 생기며, 위에선 c,b가 그러했다. 실제로 그래프를 확인해보면 b,c,d가 사이클 관계에 있는 것을 확인.
그러나 이렇게 일일히 확인하는 것은 어려우므로 그래프에서 preorder, post order 만 보고도 알 수 있는 방법을 생각해보자.

u가 조상, v가 자손인 forward edge: pre[u] < pre[v] < post[v] < post[u]
u가 자손, v가 조상인 backward edge: pre[v] < pre[u] < post[u] < post[v]
cross edge: pre[v] < post[v] < pre[u] < post[u]
DFS 코드를 보자.
1. 재귀코드

DFSAll의 마지막 for 문은 모든 노드에 대해 DFS 하기 위해서이다. (연결 성분을 다 돌자는 의도) count 는 성분 개수를 담는다.
2. 비재귀코드
