Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Advanced Competitive Programming Patterns in Interval Dynamic Programming

Tech 1

Merging Strings for Maximum Palindromes

Given two strings $S1$ and $S2$, we aim to merge them into a single string $S3$ while maintaining the relative order of characters from the original strings. The goal is to find the maximum possible length of a palindromic substring within any such $S3$.

A four-dimensional dynamic programming approach can track whether a segment formed by $S1[i..j]$ and $S2[k..l]$ constitutes a palindrome. Let dp[i][j][k][l] be a boolean indicating this property.

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

using namespace std;

int solve_palindrome_merge() {
    string s1, s2;
    if (!(cin >> s1 >> s2)) return 0;
    int n = s1.length(), m = s2.length();
    static bool dp[55][55][55][55];
    int max_len = 0;

    for (int len1 = 0; len1 <= n; ++len1) {
        for (int len2 = 0; len2 <= m; ++len2) {
            for (int i = 1; i + len1 - 1 <= n; ++i) {
                for (int k = 1; k + len2 - 1 <= m; ++k) {
                    int j = i + len1 - 1, l = k + len2 - 1;
                    bool &res = dp[i][j][k][l];
                    res = false;
                    if (len1 + len2 <= 1) res = true;
                    else {
                        if (s1[i-1] == s1[j-1] && len1 > 1) res |= dp[i+1][j-1][k][l];
                        if (s1[i-1] == s2[l-1] && len1 > 0 && len2 > 0) res |= dp[i+1][j][k][l-1];
                        if (s2[k-1] == s1[j-1] && len1 > 0 && len2 > 0) res |= dp[i][j-1][k+1][l];
                        if (s2[k-1] == s2[l-1] && len2 > 1) res |= dp[i][j][k+1][l-1];
                    }
                    if (res) max_len = max(max_len, len1 + len2);
                }
            }
        }
    }
    return max_len;
}

Sequence Selection with Positional Multipliers

Consider a sequence $A$ of length $n$. In each step, you remove a number from either the left or right end. The score for the $k$-th selection is $A_{chosen} \times B_k$, where $B$ is a sequence of multipliers. We seek to maximize the total score.

The state dp[i][j] represents the maximum score obtained from the sub-interval $[i, j]$. Since the total number of elements picked is $n - (j - i + 1)$, we can determine which multiplier $B$ to use based on the current interval length.

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

using namespace std;

long long maximize_score() {
    int n;
    cin >> n;
    vector<int> a(n + 1), b(n + 1);
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1; i <= n; ++i) cin >> b[i];

    vector<vector<long long>> dp(n + 2, vector<long long>(n + 2, 0));
    for (int len = 1; len <= n; ++len) {
        for (int i = 1; i + len - 1 <= n; ++i) {
            int j = i + len - 1;
            int multiplier = b[n - len + 1];
            dp[i][j] = max(dp[i + 1][j] + (long long)a[i] * multiplier, 
                           dp[i][j - 1] + (long long)a[j] * multiplier);
        }
    }
    return dp[1][n];
}

When scaling this to a matrix where you pick from each row independently and the multiplier is $2^k$, high-precision arithmetic is required. The logic remains the same per row, and the results are summed.

Minimum Energy Consumption for Task Scheduling

In a scenario where tasks (like shutting down street lights) are positioned along a 1D line, a worker moves at $1 m/s$. Each task consumes energy per second until it is completed. We need the minimum total energy spent.

This is a classic interval DP where the state must include the worker's current position (at the left or right boundary of the completed interval).

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

using namespace std;

const int MAXN = 1005;
long long dp[MAXN][MAXN][2]; // 0 for left, 1 for right
int pos[MAXN], weight_sum[MAXN];

void solve_energy_min() {
    int n, start_idx;
    while (cin >> n >> start_idx) {
        for (int i = 1; i <= n; ++i) {
            int w;
            cin >> pos[i] >> w;
            weight_sum[i] = weight_sum[i-1] + w;
        }
        memset(dp, 0x3f, sizeof(dp));
        dp[start_idx][start_idx][0] = dp[start_idx][start_idx][1] = 0;

        for (int len = 2; len <= n; ++len) {
            for (int i = 1; i + len - 1 <= n; ++i) {
                int j = i + len - 1;
                long long total_w = weight_sum[n] - (weight_sum[j] - weight_sum[i-1]);
                
                // Arriving at i from previous state
                dp[i][j][0] = min(dp[i+1][j][0] + (long long)(pos[i+1] - pos[i]) * (total_w + weight_sum[i+1] - weight_sum[i]),
                                  dp[i+1][j][1] + (long long)(pos[j] - pos[i]) * (total_w + weight_sum[i+1] - weight_sum[i]));
                
                // Arriving at j from previous state
                dp[i][j][1] = min(dp[i][j-1][1] + (long long)(pos[j] - pos[j-1]) * (total_w + weight_sum[j] - weight_sum[j-1]),
                                  dp[i][j-1][0] + (long long)(pos[j] - pos[i]) * (total_w + weight_sum[j] - weight_sum[j-1]));
            }
        }
        cout << min(dp[1][n][0], dp[1][n][1]) << endl;
    }
}

Maximal Sub-rectangle Sum in a 2D Grid

To find the sub-rectangle with the largest sum in an $N \times N$ grid, we compress the 2D problem into a 1D Maximum Subarray Sum problem. By fixing the top and bottom row boundaries, we sum the columns between these rows to create a 1D array.

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

using namespace std;

int max_subrectangle() {
    int n; cin >> n;
    vector<vector<int>> grid(n, vector<int>(n));
    for(int i=0; i<n; ++i) for(int j=0; j<n; ++j) cin >> grid[i][j];

    int global_max = -1e9;
    for (int top = 0; top < n; ++top) {
        vector<int> col_sums(n, 0);
        for (int bottom = top; bottom < n; ++bottom) {
            int current_kadane = 0;
            for (int k = 0; k < n; ++k) {
                col_sums[k] += grid[bottom][k];
                current_kadane = max(col_sums[k], current_kadane + col_sums[k]);
                global_max = max(global_max, current_kadane);
            }
        }
    }
    return global_max;
}

Maximal Alternating Pattern (Chessboard) Areas

For a grid of black (0) and white (1) cells, we want to find largest square and rectangle where adjacent cells always differ in color. This is solved using the "Suspension Line" (or Histogram) method.

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

using namespace std;

void checkerboard_optimization() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> mat(n + 1, vector<int>(m + 1));
    vector<vector<int>> h(n + 1, vector<int>(m + 1, 1));
    vector<vector<int>> left_bound(n + 1, vector<int>(m + 1));
    vector<vector<int>> right_bound(n + 1, vector<int>(m + 1));

    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            cin >> mat[i][j];
            left_bound[i][j] = right_bound[i][j] = j;
        }
    }

    for (int i = 1; i <= n; ++i) {
        for (int j = 2; j <= m; ++j)
            if (mat[i][j] != mat[i][j-1]) left_bound[i][j] = left_bound[i][j-1];
        for (int j = m - 1; j >= 1; --j)
            if (mat[i][j] != mat[i][j+1]) right_bound[i][j] = right_bound[i][j+1];
    }

    int max_sq = 0, max_rect = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if (i > 1 && mat[i][j] != mat[i-1][j]) {
                h[i][j] = h[i-1][j] + 1;
                left_bound[i][j] = max(left_bound[i][j], left_bound[i-1][j]);
                right_bound[i][j] = min(right_bound[i][j], right_bound[i-1][j]);
            }
            int width = right_bound[i][j] - left_bound[i][j] + 1;
            int side = min(h[i][j], width);
            max_sq = max(max_sq, side * side);
            max_rect = max(max_rect, h[i][j] * width);
        }
    }
    cout << max_sq << "\n" << max_rect << endl;
}

Efficient State Compression for Value Merging

In a sequence where adjacent equal values $v$ can be merged into $v+1$, finding the maximum possible value for a large sequence ($N \appprox 262,144$) requires optimizing space. Since values are small ($1..40$), we can use the value as part of the state.

Let dp[v][l] be the right endpoint $r$ such that the interval $[l, r]$ can be merged into a single value $v$.

#include <iostream>
#include <algorithm>

using namespace std;

int dp[60][270000];

void solve_large_merge() {
    int n; cin >> n;
    int ans = 0;
    for (int i = 1; i <= n; ++i) {
        int val; cin >> val;
        dp[val][i] = i + 1;
        ans = max(ans, val);
    }

    for (int v = 2; v < 60; ++v) {
        for (int i = 1; i <= n; ++i) {
            if (!dp[v][i] && dp[v-1][i] != 0) {
                dp[v][i] = dp[v-1][dp[v-1][i]];
            }
            if (dp[v][i]) ans = max(ans, v);
        }
    }
    cout << ans << endl;
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.