1. 程式人生 > >[HNOI2002]DNA分子的最佳比對

[HNOI2002]DNA分子的最佳比對

看著網上沒有人寫這道題的部落格,洛谷上也只有十幾個人AC,我不妨嘗試了這道題!

題解基本沒有!

任何一個偉大的思想,都有一個微不足道的開始。

題目描述

DNA分子是人類遺傳資訊的載體,它間接地指導蛋白質的合成。DNA分子是由四種核苷酸組成的長鏈,這四種核苷酸分別是腺嘌呤核苷酸(用A代表)、鳥嘌呤核苷酸(用G代表)、胞嘧啶核苷酸(用C代表)和胸腺嘧啶核苷酸(用T代表)。習慣上用一個字符集為{A,T,C,G}的字串來表示一個DNA分子序列,如CGTTAGA。

在生物進化過程中,DNA分子可能發生各種各樣的突變。這種突變形成了生物遺傳資訊的改變,從而使生物得以分化,構成了生物的多樣性。主要的突變有三種:(1)在一個DNA序列中插入一個新的核苷酸,(2)DNA序列中丟失了一個核苷酸,(3)DNA序列中的某個核苷酸被另一個核苷酸所取代。

所謂兩個DNA序列的一個比對是尋找一種排列方式,使得兩個DNA序列在同樣的位置上有相同的核苷酸,而若在同樣的位置上兩個DNA序列的核苷酸不同,則是由三種突變之一得到。例如,對兩個DNA序列T =ATCAG,T =ACTAG,可以按如下方式比對,

比對1:

T1 T2

A A T --- (T2 的該位置由T1 丟失一個核苷酸T得到,“--”表示空白)

C C

--- T (T2 的該位置由T1 插入一個核苷酸T得到)

A A

G G

也可以按如下方式比對

比對2:

T1 T2

A A

T C (T2 的該位置用一個C取代 T1 的T)

C T (T2 的該位置用一個T1取代T 的C)

A A

G G

如果兩個DNA序列在相同的位置上有越多相同的核苷酸對,則表明它們之間越相似,即它們存在功能上的相似性和進化史上的親緣關係。

對於兩個DNA序列的一個比對,規定如下得分方式:(1)一個同樣的位置上有相同的核苷酸對,則可得1分;(2)一個同樣的位置上有不同的核苷酸對,則得0分;(3)如果在某個位置上一個序列有核苷酸,而另一個序列在該位置上為“--”,則得-2分。例如,比對1的得分是0分,比對2的得分是3分。

問題是:對於兩個DNA序列,尋找一種比對方式,使得它們的得分最高。

輸入輸出格式

輸入格式:

輸入資料由檔名為INPUT.*的文字檔案提供,共有2行。

第1行為DNA序列T1 , 第2行為DNA序列T2 。序列的長度不大於1000。序列中的字母是英文小寫或者大寫字母。

輸出格式:

程式執行結束時,在螢幕上輸出所找出的最大的得分

輸入輸出樣例

輸入樣例: 
Atcag
Actag
輸出樣例: 
3



這道題我調了好久才調出來,悲慘qwq

講一講思路吧!
正經點!

這道題要懂,恐怕LCS演算法也要懂,連結:https://www.cnblogs.com/wangshengjun/p/LCS.html
這是比LCS多一點複雜一點。
f[i][j]表示第一個字元匹配到i的,第二個字元匹配到j的最優值。
f[i][j]有三種情況轉移:
第一種是 i 不變 ,j 向後移一格 , 則值 減二。(如題:如果在某個位置上一個序列有核苷酸,而另一個序列在該位置上為“--”,則得-2分。例如,比對1的得分是0分,比對2的得分是3分。)
程式碼如下:
f[i][j-1]-2
          第二種是 j不變,i向後移一格,則值減二。(如題:如果在某個位置上一個序列有核苷酸,而另一個序列在該位置上為“--”,則得-2分。例如,比對1的得分是0分,比對2的得分是3分。)
程式碼如下:
f[i-1][j]-2

                             第三種是i,j 同時向後移一格,當兩串相同時+1,否則,不更新值。

                                  程式碼如下:

f[i-1][j-1]+(s1[i]==s2[j]?1:0)

        結合之後狀態轉移方程就產生了!

f[i][j]=max(f[i-1][j-1]+(s1[i]==s2[j]?1:0),f[i-1][j]-2,f[i][j-1]-2)
                   用狀態轉移方程轉換成二維圖:
                                                        
      但還需要預處理一下,把f[i][j]的初始值賦值為(0,-2,-4,-6,-8 ,......(如此規律));
                        人間就是這麼簡單,這麼奇怪!

                        加上之後,萬事大吉了— — AC 了。

70~70~70~ WA了3個點。

              

          最後,廢話不多說,放程式碼!!!

#include<bits/stdc++.h>
int f[1039][1039],ans=-2147483648,flag;
char s1[1039],s2[1039];
int max(int a,int b,int c){return std::max(a,std::max(b,c));}
int main(){
    scanf("%s%s",s1+1,s2+1);
    int l1=strlen(s1+1);int l2=strlen(s2+1),tmp=0,l = std::max(l1,l2);
    for(int i=0;i<l+1;i++){
    	f[i][0]=tmp;
    	f[0][i]=tmp;
    	tmp-=2;
	}f[0][0] = 0;
    for(int i = 1; i < l1+1; i++)
        for(int j = 1; j < l2+1; j++){
            if(s1[i]==s2[j])flag = 1;
            else flag = 0;
            f[i][j] = max(f[i-1][j-1]+flag, f[i-1][j]-2, f[i][j-1]-2);
        }
    ans = std::max(ans, f[l1][l2]);
    printf("%d", ans);
    return 0;
}

  小心字串,很肯人的。

難道這道題就這麼難麼?
祝大家RP++!

OVER!