POJ 3280:給定字串 S(長度 M ≤ 2000),可對任意字元執行刪除(cost D[c])或插入(cost I[c]),插入相當於在對稱位置刪除。目標:最小總成本使字串變成回文。等價於:對 S[i] 和 S[j] (icost[S[j]]),取較小者。
狀態:dp[i][j] = 使 S[i..j] 變回文的最小成本。答案 dp[0][M-1]。
if (S[i] == S[j]) dp[i][j] = dp[i+1][j-1]; else dp[i][j] = min(dp[i+1][j] + cost[S[i]], dp[i][j-1] + cost[S[j]]);
轉移只依賴 dp[i+1][*](下一行),可用兩個一維陣列 cur[], nxt[] 交替,空間從 O(M²) 降為 O(M)。
對角線長度 d = j-i+1 從 2 遞增至 M。對固定 d,i 從 0 到 M-d,j = i+d-1。dp[i][j] 映射到 cur[i];dp[i+1][j-1] 映射到 nxt[i+1];dp[i+1][j] 映射到 nxt[i];dp[i][j-1] 映射到 cur[i+1]。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXM = 2005;
char S[MAXM];
int cost[26];
int dp[2][MAXM];
inline int min(int a, int b) { return a < b ? a : b; }
int solve() {
int M = strlen(S);
memset(dp, 0, sizeof(dp));
int cur = 0, nxt = 1;
for (int d = 2; d <= M; ++d) {
swap(cur, nxt);
for (int i = 0, j = d-1; j < M; ++i, ++j) {
if (S[i] == S[j])
dp[cur][i] = dp[cur][i+1]; // 實際為 nxt[i+1],但此處邏輯需修正
else {
int c1 = dp[nxt][i+1] + cost[S[i]-'a']; // dp[i+1][j]
int c2 = dp[nxt][i] + cost[S[j]-'a']; // dp[i][j-1]
dp[cur][i] = min(c1, c2);
}
}
}
return dp[cur][0];
}
// 修正版:正確的索引映射
int solve_correct() {
int M = strlen(S);
memset(dp, 0, sizeof(dp));
for (int d = 2; d <= M; ++d) {
int cur = d & 1, nxt = cur ^ 1;
for (int i = 0, j = d-1; j < M; ++i, ++j) {
if (S[i] == S[j])
dp[cur][i] = dp[nxt][i+1];
else
dp[cur][i] = min(dp[nxt][i+1] + cost[S[i]-'a'],
dp[cur][i+1] + cost[S[j]-'a']);
}
}
return dp[M&1][0];
}
int main() {
int N, M;
while (scanf("%d%d", &N, &M) == 2) {
getchar(); gets(S);
memset(cost, 0, sizeof(cost));
for (int i = 0; i < N; ++i) {
char c; int c1, c2;
scanf(" %c%d%d", &c, &c1, &c2);
cost[c-'a'] = min(c1, c2); // 刪除/插入取較小
getchar();
}
printf("%d\n", solve_correct());
}
return 0;
}
cur = d&1 確保正確引用上一輪結果