题目
思路
很正常的一道DP,如果做过LCS的话思路会比较清晰。
本题的状态转换比较复杂,所以就导致很难掌握合适的枚举顺序,又因为数据量只有100,所以用记忆化搜索直接递归会比较好写。
一会吃完饭回来有空的化写写递推。
做区间DP做糊涂了。。本题递推直接i,j正枚举就行了。。。一会回来写。。
直接写递推还是很好写,但要是想写滚动数组就得上一个档次,用二维滚动数组。
原因是本题有3个转移,j-1的转移要求枚举顺序必须为正序,i-1的转移对枚举顺序无要求,而i-1,j-1的转移,如果采用正枚举,会导致读到[i]而非[i-1]的数据。
关于二维滚动数组的详细理解和实现方法,看UVa1625。
(我就是在写本题的滚动数组的时候意识到uva1625的二维滚动数组是没有必要的。。)
1.状态定义:d(i,j),从两个序列中各取出i,j个元素,最大相似度。
2.初状态:d(0,0)=0, d(i,j) = -INF。
3.答案:d(n,m)。
4.状态转移方程:
d(i,j)=max{d(i−1,j)+D[A[i]][0], d(i,j−1)+D[B[j]][0], d(i−1,j−1)+D[A[i]][B[j]]}
d
(
i
,
j
)
=
m
a
x
{
d
(
i
−
1
,
j
)
+
D
[
A
[
i
]
]
[
0
]
,
d
(
i
,
j
−
1
)
+
D
[
B
[
j
]
]
[
0
]
,
d
(
i
−
1
,
j
−
1
)
+
D
[
A
[
i
]
]
[
B
[
j
]
]
}
(此处初值为-INF而不是0的原因是,最大相似度可能是负数,0还是太大)
代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;
const int D[5][5] = {
{0,-3,-4,-2,-1},
{-3,5,-1,-2,-1},
{-4,-1,5,-3,-2},
{-2,-2,-3,5,-2},
{-1,-1,-2,-2,5}
};
const int INF = 1 << 27;
const int maxn = 100 + 10;
char p[maxn], q[maxn]; // 从1开始
int n, m, A[maxn], B[maxn], d[maxn][maxn];
int dp(int i, int j) {
//printf("%d%d\n", i, j);
int &ans = d[i][j];
if (ans != -1) return ans;
ans = -INF;
if (i > 0) ans = max(ans, dp(i - 1, j) + D[A[i]][0]);
if (j > 0) ans = max(ans, dp(i, j - 1) + D[B[j]][0]);
if (i > 0 && j > 0) ans = max(ans, dp(i - 1, j - 1) + D[A[i]][B[j]]);
return ans;
}
int main() {
scanf("%d%s%d%s", &n, p + 1, &m, q + 1);
char c;
_rep(i, 1, n) {
c = p[i];
if (p[i] == 'A') A[i] = 1;
else if (p[i] == 'C') A[i] = 2;
else if (p[i] == 'G') A[i] = 3;
else if (p[i] == 'T') A[i] = 4;
}
_rep(i, 1, m) {
c = q[i];
if (q[i] == 'A') B[i] = 1;
else if (q[i] == 'C') B[i] = 2;
else if (q[i] == 'G') B[i] = 3;
else if (q[i] == 'T') B[i] = 4;
}
// 递归做法
memset(d, -1, sizeof(d));
d[0][0] = 0;
int ans = dp(n, m);
printf("%d\n", ans);
return 0;
}
递推二维滚动数组代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;
const int D[5][5] = {
{ 0,-3,-4,-2,-1 },
{ -3,5,-1,-2,-1 },
{ -4,-1,5,-3,-2 },
{ -2,-2,-3,5,-2 },
{ -1,-1,-2,-2,5 }
};
const int INF = 1 << 27;
const int maxn = 100 + 10;
char p[maxn], q[maxn]; // 从1开始
int n, m, A[maxn], B[maxn], d[2][maxn];
int main() {
scanf("%d%s%d%s", &n, p + 1, &m, q + 1);
char c;
_rep(i, 1, n) {
c = p[i];
if (p[i] == 'A') A[i] = 1;
else if (p[i] == 'C') A[i] = 2;
else if (p[i] == 'G') A[i] = 3;
else if (p[i] == 'T') A[i] = 4;
}
_rep(i, 1, m) {
c = q[i];
if (q[i] == 'A') B[i] = 1;
else if (q[i] == 'C') B[i] = 2;
else if (q[i] == 'G') B[i] = 3;
else if (q[i] == 'T') B[i] = 4;
}
// 递推做法
int t = 0;
_rep(i, 0, n) {
_rep(j, 0, m) {
if (i == 0 && j == 0) continue;
int &ans = d[t][j];
ans = -INF;
if (i > 0) ans = max(ans, d[t^1][j] + D[A[i]][0]);
if (j > 0) ans = max(ans, d[t][j - 1] + D[B[j]][0]);
if (i > 0 && j > 0) ans = max(ans, d[t^1][j - 1] + D[A[i]][B[j]]);
}
t ^= 1;
}
printf("%d\n", d[t^1][m]);
return 0;
}