許多問題都可拆成「先把內部子區間解完,再處理邊界/中間某一步」的型態。
常見三種模式:
原題:
https://cses.fi/problemset/task/1097
題意:
有長度 n 的數列,兩人輪流從左或右端取一個數,加到自己的分數。雙方最佳策略,問先手最多拿多少分。
解題思路:
#include <bits/stdc++.h>
using namespace std;
int main() {
    int n;
    cin >> n;
    vector<long long> a(n);
    for (int i = 0; i < n; ++i) cin >> a[i];
    // dp[l] 代表當前區間長度下的 dp[l][r](先手-後手的最大分差)
    vector<long long> dp(n, 0);
    for (int i = 0; i < n; ++i) dp[i] = a[i]; // len=1
    for (int len = 2; len <= n; ++len) {
        for (int l = 0; l + len - 1 < n; ++l) {
            int r = l + len - 1;
            long long takeL = a[l] - dp[l + 1]; // 取左,對手面對 [l+1..r]
            long long takeR = a[r] - dp[l];     // 取右,對手面對 [l..r-1]
            dp[l] = (takeL > takeR) ? takeL : takeR;
        }
    }
    long long total = 0;
    for (int i = 0; i < n; ++i) total += a[i];
    long long first = (total + dp[0]) / 2; // 先手實際分數
    cout << first << '\n';
    return 0;
}
原題:
https://cses.fi/problemset/task/1744
題意:
給長方形大小 a × b,每次只能沿格線作整條直切,問最少需要切幾刀才能把它切成若干個正方形。
解題思路:
#include <bits/stdc++.h>
using namespace std;
int main() {
    int a, b;
    cin >> a >> b;
    // dp[x][y] = x*y 矩形切成正方形的最少刀數
    vector<vector<int> > dp(a + 1, vector<int>(b + 1, 0));
    for (int x = 1; x <= a; ++x) {
        for (int y = 1; y <= b; ++y) {
            if (x == y) { dp[x][y] = 0; continue; }
            int best = INT_MAX / 4;
            // 水平切成 i 和 x-i
            for (int i = 1; i < x; ++i) {
                int cand = dp[i][y] + dp[x - i][y] + 1;
                if (cand < best) best = cand;
            }
            // 垂直切成 j 和 y-j
            for (int j = 1; j < y; ++j) {
                int cand = dp[x][j] + dp[x][y - j] + 1;
                if (cand < best) best = cand;
            }
            dp[x][y] = best;
        }
    }
    cout << dp[a][b] << '\n';
    return 0;
}
原題:
https://leetcode.com/problems/longest-palindromic-subsequence/
題意:
給字串 s,回傳其最長回文子序列(不必連續)的長度。
解題思路:
class Solution {
public:
    int longestPalindromeSubseq(string s) {
        int n = (int)s.size();
        if (n == 0) return 0;
        static int dp[1005][1005]; // n ≤ 1000 可;更大可改用 vector
        for (int i = 0; i < n; ++i) dp[i][i] = 1;
        for (int len = 2; len <= n; ++len) {
            for (int l = 0; l + len - 1 < n; ++l) {
                int r = l + len - 1;
                if (s[l] == s[r]) {
                    dp[l][r] = (l + 1 <= r - 1) ? dp[l + 1][r - 1] + 2 : 2;
                } else {
                    dp[l][r] = dp[l + 1][r] > dp[l][r - 1] ? dp[l + 1][r] : dp[l][r - 1];
                }
            }
        }
        return dp[0][n - 1];
    }
};
原題:
https://leetcode.com/problems/palindromic-substrings/
題意:
回傳字串裡共有多少個回文子串(必須連續)。
解題思路:
class Solution {
public:
    int countSubstrings(string s) {
        int n = (int)s.size();
        if (n == 0) return 0;
        static char dp[1005][1005]; // 用 char 節省空間:0/1
        int ans = 0;
        for (int len = 1; len <= n; ++len) {
            for (int l = 0; l + len - 1 < n; ++l) {
                int r = l + len - 1;
                if (s[l] == s[r]) {
                    if (r - l <= 2) dp[l][r] = 1;                 // 1 或 2 或 3 長度時,頭尾相同即回文
                    else dp[l][r] = dp[l + 1][r - 1];
                } else dp[l][r] = 0;
                if (dp[l][r]) ++ans;
            }
        }
        return ans;
    }
};