题目大意
给定两串 DNA 序列,可以在其中任意插空格,然后逐位比较,当两位都是字母时查表得到相似度。
长度为 $k$ 的空格有额外相似度 $- A - B(k - 1)$。求两序列的最大相似度。
分析
若不考虑空格的贡献,容易想到二维 DP,记 ${\rm DP}[i,j]$ 是 $A$ 串到位置 $i$ 同时 $B$ 串到位置 $j$ 时最大的相似度,有
$$ {\rm DP}[i, j] = \max\{ {\rm DP}[i - 1, j - 1] + D[i, j], {\rm DP}[i - 1, j], {\rm DP}[i, j - 1] \} $$
当我们考虑空格的贡献时,可以发现空格的额外贡献只与前面一位有关。若两个序列此位都是空格,则把去掉后相似度一定会增加,故只有三种可能:设 ${\rm DP}_0$ 是结尾没有空格;${\rm DP}_1$ 是空格在 $A$ 串;${\rm DP}_2$ 是空格在 $B$ 串。
思考最后一个空格的转移方式,自然有方程
$$ \begin{aligned} {\rm DP}_0[i, j] &= \max\{ {\rm DP}_0[i - 1, j - 1], {\rm DP}_1[i - 1, j - 1], {\rm DP}_2[i - 1, j - 1] \} + D[i, j] \\ {\rm DP}_1[i, j] &= \max\{ {\rm DP}_0[i, j - 1] - A, {\rm DP}_1[i, j - 1]- B, {\rm DP}_2[i, j - 1] - A\} \\ {\rm DP}_2[i, j] &= \max\{ {\rm DP}_0[i - 1, j] - A, {\rm DP}_1[i - 1, j] - A, {\rm DP}_2[i, j - 1] - B \} \\ \end{aligned} $$
随手加滚动数组 WA 了好久,发现每次都要清空为 -INF,否则会 WA。
#include "template/index.hpp"
const int maxn = 3000 + 10;
int dp_1[maxn][3], dp_2[maxn][3];
char sa[maxn], sb[maxn];
int D[5][5];
int DNA(char c); // AGTC -> 1..4
int main() {
scanf("%s %s", sa + 1, sb + 1);
int n = strlen(sa + 1), m = strlen(sb + 1);
for (int i = 1; i <= 4; i++)
for (int j = 1; j <= 4; j++)
D[i][j] = rr();
int A = rr(), B = rr(), *p;
memset(dp_1, -0x7f, sizeof(dp_1));
memset(dp_2, -0x7f, sizeof(dp_2));
auto &dp1 = dp_1, &dp2 = dp_2;
dp2[0][0] = 0;
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
int da = DNA(sa[i]), db = DNA(sb[j]);
if (i > 0 && j > 0) {
p = dp1[j - 1];
dp2[j][0] = max3(p[0], p[1], p[2]) + D[da][db];
}
if (i > 0) {
p = dp1[j];
dp2[j][2] = max3(p[0] - A, p[1] - A, p[2] - B);
}
if (j > 0) {
p = dp2[j - 1];
dp2[j][1] = max3(p[0] - A, p[1] - B, p[2] - A);
}
}
swap(dp1, dp2);
memset(dp2, -0x7f, sizeof(dp2));
}
p = dp1[m];
int ans = max3(p[0], p[1], p[2]);
printf("%d\n", ans);
return 0;
}