Dynamic Programming compilation

Dynamic Programming Compilation

DYNAMIC PROGRAMMING COMPILATION

Basic Recurrence Relation (Fibonacci type)

a[i+1] constrained on a[i] (=> f[n][d], a[i] taking values 0 to d-1)

Kleinberg Exercises - 4, 10
  • 1. 1220. Count Vowels Permutation
  • 2. 935. Knight Dialer
  • 3. Vacation (Atcoder)
  • 4. Array Description (CSES)
  • 5. Подкрутка II (*)
  • 6. M. Medical Parity
    Spoiler dp[i][j] = min cost over x'[1..i], y'[1..i] such that y[i] == j, j belongs to {0, 1}. Observe that y[i-1] = (y[i] - x[i]) % 2
  • 7. C. Wonderful City
    Spoiler Row and column imbalance are independent.
     
    // solving row imbalance
    // use b
    std::vector<std::array<ll, 2>> dp(n+1);
    dp[0][0] = dp[0][1] = 0;
    dp[1][0] = 0; dp[1][1] = b[1];
    for (int i = 2; i <= n; i++) {
        // dp[i][0] -> not changing ith col
        // checking if ith col equals (i-1)th col before change and after change
        bool check1 = true, check2 = true, check3 = true;
        for (int r = 1; r <= n; r++) {
            if (h[r][i-1] == h[r][i]) check1 = false;
            if (h[r][i-1] + 1 == h[r][i]) check2 = false;
            if (h[r][i-1] == h[r][i] + 1) check3 = false;
    
            if (!check1 && !check2 && !check3) break;
        }
        dp[i][0] = INF;
        if (check1) dp[i][0] = std::min(dp[i][0], dp[i-1][0]);
        if (check2) dp[i][0] = std::min(dp[i][0], dp[i-1][1]);
        // dp[i][1] -> increasing ith col by 1
        dp[i][1] = INF;
        if (check1) dp[i][1] = std::min(dp[i][1], dp[i-1][1] + b[i]);
        if (check3) dp[i][1] = std::min(dp[i][1], dp[i-1][0] + b[i]);
    }
    
    ll row_sum = std::min(dp[n][0], dp[n][1]);
    
    // solving column imbalance
    // use a
    dp[0][0] = dp[0][1] = 0;
    dp[1][0] = 0; dp[1][1] = a[1];
    for (int i = 2; i <= n; i++) {
        // dp[i][0] -> not changing ith row
        // checking if ith col equals (i-1)th row before change and after change
        bool check1 = true, check2 = true, check3 = true;
        for (int c = 1; c <= n; c++) {
            if (h[i-1][c] == h[i][c]) check1 = false;
            if (h[i-1][c] + 1 == h[i][c]) check2 = false;
            if (h[i-1][c] == h[i][c] + 1) check3 = false;
    
            if (!check1 && !check2 && !check3) break;
        }
        dp[i][0] = INF;
        if (check1) dp[i][0] = std::min(dp[i][0], dp[i-1][0]);
        if (check2) dp[i][0] = std::min(dp[i][0], dp[i-1][1]);
        // dp[i][1] -> increasing ith col by 1
        dp[i][1] = INF;
        if (check1) dp[i][1] = std::min(dp[i][1], dp[i-1][1] + a[i]);
        if (check3) dp[i][1] = std::min(dp[i][1], dp[i-1][0] + a[i]);
    }
    
    ll col_sum = std::min(dp[n][0], dp[n][1]);
    
    ll total = row_sum + col_sum;
    if (total >= INF) std::cout << "-1\n";
    else std::cout << total << '\n';
                               

DP on INTERVALS

dp[i][j] = max(dp[i][t] + dp[t+1][j] + f(i, t, j)) for all i <= t < j
Matrix Chain multiplication
RNA Secondary structure using base pair maximization

Single choice DP

dp[j] = max(wj + dp[i], dp[j-1]) -> OBSERVE THAT FOR ALL IS NOT WRITTEN HERE, SO O(N)
Kleinberg Exercises - 1, 2, 11
  • 1. Maximum profit in Job Scheduling
  • 2. Maximum Earnings From Taxi
  • 3. F. Consecutive Subsequence
    Spoiler dp[a[i]] = std::max(dp[a[i]], 1 + dp[a[i] - 1]) store dp as std::map<int, int>
  • 4. E. Three Strings
    Spoiler dp[i][j] = min(dp[i-1][j] + (a[i] != c[i+j]), dp[i][j-1] + (b[j] != c[i+j]))
  • 5. C. Palindrome Basis
    Spoiler
     
    	for (int i = 0; i < N; i++) {
            if (isPalindrome(i)) palindromes.push_back(i);
        }
        int M = palindromes.size();
        std::vector<std::vector<ll>> dp(N+1, std::vector<ll>(M, 0));
        for (int i = 1; i < M; i++) {
            dp[1][i] = 1;
            dp[0][i] = 1;
        }
        for (int num = 2; num <= N; num++) {
            for (int lim = 1; lim < M; lim++) {
                if (palindromes[lim] > num) dp[num][lim] = dp[num][lim - 1];
                else {
                    dp[num][lim] = (dp[num][lim-1] + dp[num - palindromes[lim]][lim]) % MOD;
                }
            }
        }
                                 
  • 6. Minimum ascii delete sum for two strings
  • 7. E. Block Sequence
    Spoiler
     
                if (i + a[i] < n) dp[i] = min(1 + dp[i+1], dp[i + a[i] + 1]);
                else dp[i] = 1 + dp[i+1];
                				
  • 8. Maximal Square (*)
    Count Square Submatrices with All Ones
    Spoiler dp[i][j] = 0 if mat[i][j] == 0 else 1 + min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1])
    maxi = max(dp[i][j])
    ans1 = maxi * maxi
    ans2 = sum(dp[i][j])
  • 9. D. Make Them Equal(*)
    Spoiler apply dp twice
  • 10. H. Don't Blame Me
    Spoiler dp[i][j] = # subsequences using the first i elements that have AND value of j
    For the ith element we have the following choices -
    • Dont choose -> +dp[i-1][j]
    • Choose -> (a[i] == j) -> +1, or +dp[i-1][j']
     
    for (int i = 1; i <= n; i++) {
        for (int j = 63; j >= 0; j--) {
            if (a[i] == j) dp[j][i] = (dp[j][i] + 1) % NUM;
            dp[j & a[i]][i] = (dp[j & a[i]][i] + dp[j][i-1]) % NUM;
            dp[j][i] = (dp[j][i] + dp[j][i-1]) % NUM;
        }
    }
    ll ans = 0;
    for (int i = 0; i < 64; i++) {
        if (__builtin_popcount(i) == k) ans = (ans + dp[i][n]) % NUM;
    }
                                 

Multi choice DP

dp[j] = min(dp[j], Cij + dp[i]) for all 1 <= i <= j
Segmented least squares
Kleinberg Exercises - 3, 5, 6, 8, 9, 12, 15, 16(**), 17
  • 1. 132. Palindrome Partitioning II
  • 2. E. Sending a Sequence Over the Network
    Spoiler dp[n] = dp[n-bn-1] || dp[n-x-1] where x is such that b[n-x] = x (precompute this x for reducing TC)
  • 3. LIS (longest increasing subsequence) variant
    • 1. Mukhammadali and the Smooth Array
    • 2. B. Orac and Models
      Spoiler
       
      #include <iostream>
      #include <vector>
      #include <algorithm>
      using ll = long long;
      int N = 1e5 + 10;
      std::vector<std::vector<int>> divisors(N);
      int main() {
          std::ios::sync_with_stdio(false);
          std::cin.tie(nullptr);
          for (int i = 1; i < N; i++) {
              for (int j = 2 * i; j < N; j += i) divisors[j].push_back(i);
          }
          int tt; std::cin >> tt;
          while (tt--) {
              int n; std::cin >> n;
              std::vector<ll> s(n+1);
              std::vector<int> dp(n+1, 1);
              for (int i = 1; i < n; i++) std::cin >> s[i];
              for (int i = 2; i <= n; i++) {
                  int maxi = 0;
                  for (int j : divisors[i]) if (s[j] < s[i]) maxi = std::max(maxi, dp[j]);
                  dp[i] = 1 + maxi;
              }
              std::cout << *max_element(dp.begin(), dp.end()) << std::endl;
          }
          return 0;
      }
                                  

DP on Trees

 
                        
void dfs(int u, int p = -1) {
    dp[u] = base_case;   // initialize
    for (int v : adj[u]) {
        if (v == p) continue;
        dfs(v, u);
        dp[u] = combine(dp[u], dp[v]);  // merge child info
    }
}
                         

Comments