题目描述:
众所周知,人类基因可以被简单认为是一个字符串,包含四种分别用A,C,T,G表示的核苷酸。生物学家对鉴别人类基因核确定他们的功能很感兴趣。因为这对诊断人类疾病和开发新药很有用。 人类基因可以用一堆特别的快速的试验来鉴别,而且通常要借助电脑的帮助一旦基因序列测定了,下一步就可以确定它的功能了。生物学家确定一个新鉴定了的基因的功能的方法之一是在在基因数据库里和其他基因对照。要搜索的数据库里储存了很多的基因和它们的功能--很多研究人员提交了他们的研究基因和功能到数据库,而数据库是在互联网上公开的。
数据库会返回一堆最相近基因。生物学家们假设类似的基因表示类似的功能。所以新基因的功能也包含在列表里的基因里。所以严格确定最相近的一个对生物试验非常必要。
你的任务是写一个程序来按以下规则比较两个基因和决定他们的相似程度。给出两个基因 AGTGATG 和 GTTAG,他们有多相似呢?一个测量两个基因相似程度的方法就叫做alignment。在alignment里, 如果必要是可以在基因的适当位置插进空格以令他们的长度相等。例如,一个空格插进了AGTGATG以后就得到AGTGAT-G,三个空格插进了GTTAG就得到–GT--TAG。空格用减号(-)表示。现在两个串的长度就相等了。现在排在一齐就成了:
AGTGAT-G
-GT--TAG
在这个alignment里,有四个基因是相配的:第二位的G,第三位的 T,第六位的T,和第八位的G。每对排列排列的字母用一下的矩阵分配了不同的分值。
* 表示空格对空格是不允许的。所以以上这个alignment的分值是(-3)+5+5+(-2)+(-3)+5+(-3)+5=9 。
当然,其他alignments也是有可能的。一下有另一种排列 (不同数目的空格插进不同的位置):
AGTGATG
-GTTA-G
这个alignment 给出了的分值是 (-3)+5+5+(-2)+5+(-1) +5=14。 所以这一个比前一个要好。没有其他的alignment有更高的分值了,所以说这两个基因的相似程度是14。
输入格式
输入数据有T组测试数据。测试数据的数目 (T)在输入的第一行给出。每组测试数据有两行:每行有一个表示基因长度的整数和一个基因序列。每个基因的长度都不超过100.。
输出格式
输出只要输出每组测试数据的相似程度,每组一行。
Sample Input
2
7 AGTGATG
5 GTTAG
7 AGCTATT
9 AGCTTTAAA
Output for the Sample Input
14
21
题目分析:
观察题目给出的一个最优解:
AGTGATG
-GTTA-G
将其从某一处切开,如果左边部分的分值不是最大,那么将其进行调整,使其分值变大,则整个解分值变大,与已知的最优矛盾。所以左边部分的分值必是最大。同理,右边也是。可见满足最优子结构的性质。考虑使用DP:
设两个DNA序列分别为s1,s2,长度分别为len1,len2,score为分值表。f[i,j]表示子串s1[1..i]和s2[1..j]的分值。考虑一个f[i,j],我们有:
1.s1取第i个字母,s2取“-”:f[i-1,j] + score[s1[i],'-']
2.s1取“-”,s2取第j个字母:f[i,j-1] + score['-',s2[j]]
3.s1取第i个字母,s2取第j个字母:f[i-1,j-1] + score[s1[i],s2[j]]
即f[i,j] = max(f[i-1,j] + score[s1[i],'-'], f[i,j-1] + score['-',s2[j]], f[i-1,j-1] + score[s1[i],s2[j]]);
然后考虑边界条件,这道题为i或j为0的情况。
当i=j=0时,即为f[0,0],这是在计算f[1,1]时用到的,根据f[1,1] = f[0,0] + score[s1[i], s2[j]],明显有f[0,0] = 0。
当i=0时,即为f[0,1..len2],有了f[0,0],可以用f[0,j] = f[0,j-1] + table['-',s2[j]]来计算。
当j=0时,即为f[1..len1,0],有了f[0,0],可以用f[i,0] = f[i-1,0] + table[s1[i],'-']来计算。
至于计算顺序,只要保证计算f[i,j]的时候,使用到的f[i-1,j],f[i,j-1],f[i-1,j-1]都计算出来了就行了。所谓划分阶段也就是为了达到这个目的。这样我们使用一个二重循环就可以了。
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
int score[5][5]={{5,-1,-2,-1,-3},
{-1,5,-3,-2,-4},
{-2,-3,5,-2,-2},
{-1,-2,-2,5,-1},
{-3,-4,-2,-1,0}};
int f[101][101];
int num(char ch)
{
if(ch=='A')
return 0;
if(ch=='C')
return 1;
if(ch=='G')
return 2;
if(ch=='T')
return 3;
if(ch=='-')
return 4;
}
int max(int x,int y,int z)
{
int temp=-100,i;
if(x>temp) temp=x;
if(y>temp) temp=y;
if(z>temp) temp=z;
return temp;
}
int main()
{ //freopen("input.txt","r",stdin);
//freopen("result.txt","w",stdout);
int cases,n1,n2,i,j;
int x,y,z;
string s1,s2;
cin>>cases;
while(cases--)
{
cin>>n1;getchar();cin>>s1;cin>>n2;getchar();cin>>s2;
f[0][0]=0;
for(i=1;i<=n1;i++)
f[i][0]=f[i-1][0]+score[num(s1[i-1])][4];
for(j=1;j<=n2;j++)
f[0][j]=f[0][j-1]+score[4][num(s2[j-1])];
for(i=1;i<=n1;i++)
for(j=1;j<=n2;j++)
{
x=f[i-1][j]+score[num(s1[i-1])][4];
y=f[i][j-1]+score[4][num(s2[j-1])];
z=f[i-1][j-1]+score[num(s1[i-1])][num(s2[j-1])];
f[i][j]=max(x,y,z);
}
cout<<f[n1][n2]<<endl;
}
return 0;
}