CF49E Common ancestor 区间DP
一.题目
传送门
翻译:
Berland的每一个生物的DNA序列可以被表示成一个由小写字母组成的非空字符串。Berland的科学家们发现所有生物都是一步一步进化来的。在其中的每一步,DNA序列的一个字符会被替换成另外的两个。总共有nn种允许的变化。变化表示一个字符可以被替换成两个字符每一种变化均可以无限次发生。 科学家们表示,如果一个DNA序列在整个进化过程中可以最终变为 和 或许是经过了不同的步骤数,那么DNA序列分别为 和的两个生物就会有一个共同祖先。现在给出 和,你的任务是弄清楚分别拥有这两种DNA序列的生物是否有共同祖先。如果存在,你需要找出所有共同祖先中长度最短的DNA序列。
二.题解
这道题的数据范围为所以必定很暴力,先考虑一个的算法:
然后考虑转移:
方程式就是这样,下面还有一个预处理,处理出,这是一个时间复杂度为的预处理,也很暴力,我们把这个预处理存在一个数组里。
我们要用二进制来存这些字母,,用int就行。
这道题就做完了。
三.Code
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define M 55
#define INF 0x3f3f3f3f
int n, len1, len2, mp[M][M], f[M][M][5], dp[M][M];
char s1[M], s2[M];
void prepare (char s[], int len, int type){
for (int i = 1; i <= len; i ++)
f[i][i][type] |= 1 << (s[i] - 'a');
for (int r = 1; r <= len; r ++){
for (int l = r - 1; l >= 1; l --){
for (int k = l; k < r; k ++){
for (int i = 0; i < 26; i ++){
if (! (f[l][k][type] >> i & 1))
continue;
for (int j = 0; j < 26; j ++){
if (! (f[k + 1][r][type] >> j & 1))
continue;
f[l][r][type] |= mp[i][j];
}
}
}
}
}
}
int main (){
scanf ("%s%s", s1 + 1, s2 + 1);
len1 = strlen (s1 + 1);
len2 = strlen (s2 + 1);
scanf ("%d", &n);
char tmp[10];
for (int i = 1; i <= n; i ++){
scanf ("%s", tmp);
mp[tmp[3] - 'a'][tmp[4] - 'a'] |= 1 << (tmp[0] - 'a');
}
prepare (s1, len1, 0);
prepare (s2, len2, 1);
for (int i = 0; i <= len1; i ++)
for (int j = 0; j <= len2; j ++)
dp[i][j] = INF;
dp[0][0] = 0;
for (int i = 1; i <= len1; i ++){
for (int j = 1; j <= len2; j ++){
for (int k1 = 1; k1 <= i; k1 ++){
for (int k2 = 1; k2 <= j; k2 ++){
if (! (f[k1][i][0] & f[k2][j][1]))
continue;
if (dp[k1 - 1][k2 - 1] == INF)
continue;
dp[i][j] = min (dp[i][j], dp[k1 - 1][k2 - 1] + 1);
}
}
}
}
if (dp[len1][len2] == INF)
printf ("-1\n");
else
printf ("%d\n", dp[len1][len2]);
return 0;
}
浙公网安备 33010602011771号