許多問題都可拆成「先把內部子區間解完,再處理邊界/中間某一步」的型態。
常見三種模式:
原題:
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;
}
};