zoukankan      html  css  js  c++  java
  • 洛谷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;
     } 
    
    
    
    
    
     
  • 相关阅读:
    冒泡排序&快速排序
    1252. Cells with Odd Values in a Matrix
    位运算小结
    832. Flipping an Image
    1812. Determine Color of a Chessboard Square
    10、属性、构造函数与析构函数
    09、封装与类成员
    07、面向对象简介
    06、C#异常处理
    03、运算符
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/12271431.html
Copyright © 2011-2022 走看看