洛谷P1140 相似基因(线性DP)

题目背景

大家都知道,基因可以看作一个碱基对序列。它包含了444种核苷酸,简记作A,C,G,TA,C,G,TA,C,G,T。生物学家正致力于寻找人类基因的功能,以利用于诊断疾病和发明药物。

在一个人类基因工作组的任务中,生物学家研究的是:两个基因的相似程度。因为这个研究对疾病的治疗有着非同寻常的作用。

题目描述

两个基因的相似度的计算方法如下:

对于两个已知基因,例如AGTGATGAGTGATGAGTGATG和GTTAGGTTAGGTTAG,将它们的碱基互相对应。当然,中间可以加入一些空碱基-,例如:

这样,两个基因之间的相似度就可以用碱基之间相似度的总和来描述,碱基之间的相似度如下表所示:

那么相似度就是:(−3)+5+5+(−2)+(−3)+5+(−3)+5=9(-3)+5+5+(-2)+(-3)+5+(-3)+5=9(3)+5+5+(2)+(3)+5+(3)+5=9。因为两个基因的对应方法不唯一,例如又有:

相似度为:(−3)+5+5+(−2)+5+(−1)+5=14(-3)+5+5+(-2)+5+(-1)+5=14(3)+5+5+(2)+5+(1)+5=14。规定两个基因的相似度为所有对应方法中,相似度最大的那个。

输入格式

共两行。每行首先是一个整数,表示基因的长度;隔一个空格后是一个基因序列,序列中只含A,C,G,TA,C,G,TA,C,G,T四个字母。1≤1 le 1≤序列的长度≤100 le 100100。

输出格式

仅一行,即输入基因的相似度。

输入输出样例

输入 #1 
7 AGTGATG
5 GTTAG
输出 #1 
14
首先放上一篇超棒的题解:https://www.luogu.com.cn/paste/u7l8dqnn
这是一道线性DP题,蓝书上说得好,如果一个动态规划的算法包含多个维度,但在每个维度上都具有线性变化的阶段,这同样称为线性DP。这道题一看有两个字符串,联想到另一道题“编辑距离”,可以想到要开一个二维数组来存储,即dp[i][j]表示a串的1到i个碱基与b串的1到j个碱基的相似度。状态找到后开始写转移方程。由题意得,不考虑边界的话一共有三种情况,即dp[i][j]可能等于:
1.dp[i-1][j-1]+rela(a[i],b[j]).这表示a[i]与b[j]两个碱基彼此配对,其中rela(p,q)表示碱基p和碱基q的相似度。
2.dp[i-1][j]+rela(a[i],' ').这表示a[i]与空碱基配对。这里要注意到动态规划里无后效性的概念,不用去管a的前i-1个碱基与b的前j个碱基如何配对,只需要分析眼前情况。
3.dp[i][j-1]+rela(b[j],' ').这表示b[j]与空碱基配对。
最终要在这三者中取最大就得到转移方程。输出的答案存在dp[lena][lenb]中。lena,lenb分别表示a,b串的长度。
#include <bits/stdc++.h>
using namespace std;
char a[105],b[105];
int lena,lenb;
int dp[210][210]={-20000000}; //dp[i][j]表示a的第i个与b的第j个到之前的相似度 
map<char,int>m;
int pos=0;
int rela[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 mmax(int a,int b,int c)
{
    return max(max(a,b),c);
}
int main()
{
    scanf("%d%s",&lena,a);
    scanf("%d%s",&lenb,b);
    int i,j;
    m['A']=0;//字典映射碱基到对应的下标,方便获得相似度 
    m['C']=1;
    m['G']=2;
    m['T']=3;
    m[' ']=4;
    dp[0][0]=0;
    for(i=1;i<=lena;i++)//边界只有一种情况 
    {
        dp[i][0]=dp[i-1][0]+rela[m[a[i-1]]][4];
    }
    for(j=1;j<=lenb;j++)
    {
        dp[0][j]=dp[0][j-1]+rela[4][m[b[j-1]]];
    }
    for(i=1;i<=lena;i++)
    {
        for(j=1;j<=lenb;j++)
        {
            if(i&&j)
            {    
                dp[i][j]=mmax(//手写的三个数取最大的mmax函数 
                dp[i-1][j-1]+rela[m[a[i-1]]][m[b[j-1]]],
                dp[i-1][j]+rela[m[a[i-1]]][4],
                dp[i][j-1]+rela[4][m[b[j-1]]]            
                        );
            }
        }
    }
    cout<<dp[lena][lenb];//输出答案。这里注意不要习惯性的写成dp[lena-1][lenb-1],再次回顾dp[i][j]的定义,是“第i个” 
    return 0;
 } 



 
原文地址:https://www.cnblogs.com/lipoicyclic/p/12271431.html