21735 c++ - 눈덩이 굴리기

2024. 11. 9. 22:08🐣/BOJ

백트래킹 문제입니다. 또는 DP로도 풀 수 있습니다.

 

[Swift] BackTracking / 백트래킹 / 퇴각검색

BackTracking / 백트래킹 / 퇴각검색 여러 후보 해 중에서 특정 조건을 충족시키는 모든 해를 찾는 알고리즘. 해를 찾는 도중 막히면 돌아가 다시 해를 찾아간다. 1. 해를 찾아가는 과정은 '루트'에서

chanhhh.tistory.com

 

 

[종만북] Dynamic Programming / DP / 동적 계획법

Algorithmic Problem Solving Strategies  개인적으로 읽고 정리한 요약입니다. 아래 링크에 문제 풀이 예제가 있습니다. GitHub - chanheki/AlgorithmicProblemSolvingStrategies: Algorithm Book Study Algorithm Book Study. Contribute t

chanhhh.tistory.com

 

처음에 끝에 도달한 경우에 대해서 기저조건을 설정해 두지 않아서 틀렸습니다. m의 크기가 작아서 그냥 dfs로 모든 경우를 백트래킹으로 풀었습니다. dfs로 푼 이후에, DP로도 풀 수 있을 거 같아서 풀어보고 나서 둘의 차이점에 대해서 생각해 보았습니다.

DFS와 DP의 차이

  • DFS:
    • 재귀 호출을 통해 모든 가능한 경로를 탐색하면서 최적해를 찾습니다.
    • 특정 경로가 중복될 가능성이 높아, 같은 계산을 반복하게 됩니다. 이를 방지하기 위해 메모이제이션을 추가할 수 있지만, 재귀 깊이가 깊어지면 스택 오버플로우가 발생할 수 있습니다.
    • 시간 복잡도: 모든 경로를 탐색하므로 최악의 경우 O(2^M)이 됩니다. (경우의 수는 각 초마다 2가지 선택을 하기 때문)
  • DP (Bottom-Up):
    • DP에서는 현재 가능한 상태들(dp[d][i])을 순차적으로 갱신하며, 이전 단계의 최적해를 재활용하여 중복 계산을 피합니다.
    • 재귀 호출이 없어 스택 오버플로우가 발생하지 않습니다.
    • 시간 복잡도: 모든 상태를 한번씩만 계산하므로 O(N * M)이 됩니다. DFS에 비해 훨씬 효율적입니다.

요약

  • DFS는 재귀적으로 모든 경로를 탐색하는 방식이며, Bottom-Up DP는 중복 계산을 피하고 한 번의 순차적 계산으로 최적해를 찾는 방식입니다. O(N*M) > O(2^M)
  • Bottom-Up DP 방식은 DFS 대비 더 효율적이며, 특히 M이 클수록 성능 차이가 더 커집니다.

따라서 이 문제의 경우, DP 방식이 훨씬 빠르고 메모리 사용도 효율적입니다. m의 범위가 10보다 더 컸으면 그냥 dfs로는 못풀었을 겁니다.

 


백트래킹으로 푼 해답.

#include <algorithm>
#include <iostream>
#include <vector>

#define FASTIO ios::sync_with_stdio(0), cin.tie(0)
using namespace std;

int n, m, maxCount = 0;

void dfs(vector<int> a, int d, int i, int sum) {
  if (d == m || i >= n) {
    maxCount = max(sum, maxCount);
    return;
  }

  dfs(a, d + 1, i + 1, sum + a[i + 1]);
  dfs(a, d + 1, i + 2, sum / 2 + a[i + 2]);
}

int main() {
  FASTIO;

  cin >> n >> m;
  vector<int> a(n + 1, 0);
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }

  dfs(a, 0, 0, 1);

  cout << maxCount;

  return 0;
}

 

DP로 푼 해답. Bottom UP방식으로 풀면된다.

#include <algorithm>
#include <iostream>
#include <vector>

#define FASTIO ios::sync_with_stdio(0), cin.tie(0)
using namespace std;

int main() {
  FASTIO;

  int n, m;
  cin >> n >> m;
  vector<int> a(n + 1, 0);
  vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }

  dp[0][0] = 1;
  int maxCount = 0;

  for (int d = 1; d <= m; d++) {
    for (int i = 0; i <= n; i++) {
      if (dp[d - 1][i] == 0) continue;

      if (i == n) {
        maxCount = max(maxCount, dp[d - 1][i]);
        continue;
      }

      if (i + 1 <= n) 
        dp[d][i + 1] = max(dp[d][i + 1], dp[d - 1][i] + a[i + 1]);
      if (i + 2 <= n)
        dp[d][i + 2] = max(dp[d][i + 2], dp[d - 1][i] / 2 + a[i + 2]);
    }
  }

  for (int i = 1; i <= n; i++) {
    maxCount = max(maxCount, dp[m][i]);
  }

  cout << maxCount;

  return 0;
}

 

'🐣 > BOJ' 카테고리의 다른 글

30469 c++ - 호반우가 학교에 지각한 이유 2  (1) 2024.11.12
25511 c++ - 값이 k인 트리 노드의 깊이  (0) 2024.11.10
2623 c++ - 음악프로그램  (0) 2024.11.08
2239 c++ - 스도쿠  (0) 2024.10.31
1005 c++ - ACM Craft  (0) 2024.10.30