[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; }
小心字串,很肯人的。