【NOIP 模拟题】[山东多校联合模拟赛 day1 T2] 祖先(dp)
2.祖先(ancestor.cpp/c/pas)
【问题描述】任何一种生物的 DNA 都可以表示为一个由小写英文字母组成的非空字符串。科学家发现,所有的生物都有可能发生变异。所谓变异,就是子代的 DNA 串与父代的 DNA 串有差异。每次变异, DNA 串中恰好有一个字符会变成两个任意的字符。一共有 n 种可能的变异。
变异 ai->bici 表示字符 ai 有可能变异为两个字符 bici。
详细来说,就是删掉一个字符 ai,之后在原来 ai 的位置处,插入 bi,ci 两个字符(注意字符 bi 必须在 ci 的前面)。每种变异都有可能发生任意多次。可以发现,每变异一次,DNA
串的长度会加 1。
如果有一种生物 a,他的 DNA 串是 s1,另外存在一种生物 b,他的 DNA 串是 s2。如果 s2 可以通过若干次变异变为 s1,那么生物 b 就被叫做生物 a 的祖先。
现在,给定一种生物,他的 DNA 串是 s。请找出他的一个祖先,且这个祖先的 DNA 串尽量短。
【输入】
输入文件 ancestor .in,共 n+2 行。
第一行包含一个非空字符串 s。
第二行含有一个整数 n,表示所有可能的变异。
接下来 n 行,每行描述一种可能的变异,按照 ai->bici 的格式。s,ai,bi,ci 仅包含小写英文字母。
请注意:一种变异可能出现多次。
【输出】
输出文件名为 ancestor .out。
输出只有一行,一个整数,表示祖先 DNA 串的最短长度。
【输入输出样例 1】
ancestor.in
ababa
2
c->ba
ancestor.out
2
【输入输出样例 2】
ancestor.in
ababa
7
c->ba
c->cc
e->ab
z->ea
b->ba
d->dd
d->ab
ancestor.out
1
【数据范围】
对于 30%的数据,s 的长度<=5,N <= 3;
对于 100%的数据,s 的长度<=50,N <= 50
——————————————————————————————————————————————————————————————
【题解】【dp】
【ch[i][j][k]表示i-j位能否是k;list[i][1]表示第i种替换后的字符,list[i][2]、list[i][3]表示第i种替换前的字符;f[i][j]表示i-j位替换后是几个字符】
【先预处理出ch数组,然后根据ch数组进行dp,最后输出f[1][len]】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[60];
int ch[60][60][30],list[60][5],f[60][60];
int len,n;
int main()
{
freopen("ancestor.in","r",stdin);
freopen("ancestor.out","w",stdout);
int i,j,k,l;
scanf("%s",s+1);
len=strlen(s+1);
for(i=1;i<=len;++i) ch[i][i][s[i]-'a']=1;
scanf("%d\n",&n);
for(i=1;i<=n;++i)
{
char ss[10];
gets(ss);
list[i][1]=ss[0]-'a';
list[i][2]=ss[3]-'a';
list[i][3]=ss[4]-'a';
}
for(i=len;i>0;--i)
for(j=i+1;j<=len;++j)
for(k=i;k<j;++k)
for(l=1;l<=n;++l)
if(ch[i][k][list[l][2]]&&ch[k+1][j][list[l][3]]) ch[i][j][list[l][1]]=1;
for(i=len;i>0;--i)
for(j=i;j<=len;++j)
{
bool b=0;
for(k=0;k<26;++k)
if(ch[i][j][k]) f[i][j]=1,b=1;
if(!b)
{
f[i][j]=0x7fffffff;
for(k=i;k<j;++k)
if(f[i][k]+f[k+1][j]<f[i][j]) f[i][j]=f[i][k]+f[k+1][j];
}
}
printf("%d\n",f[1][len]);
return 0;
}