•参考资料
[1]:https://www.cnblogs.com/real-l/p/9712029.html
[2]:https://www.luogu.org/problemnew/solution/P1140
•题解
方法一:枚举所有可能(记忆型DP)
相关变量解释:
m,n...................................................分别代表串1、串2的长度
a[maxn]............................................a[i] : 串 1 的 i 位置的字母对应的数字(下标从 1 开始,并且 A->1;C->2;G->3;T->4)
b[maxn]............................................b[i] : 串 2 的 i 位置的字母对应的数字(解释同上)
dp[maxn][maxn]................................dp[ i ][ j ] : 串 1 的 1~i 与 串 2 的 1~j 匹配所获得的最大相似度;
table[maxn][maxn]............................对应题干中的基因配对表
步骤:
(1):预处理出dp[ i ][0] , dp[0][ j ](1 ≤ i ≤ m , 1 ≤ j ≤ n)
1 for(int i=1;i <= m;++i) 2 dp[i][0]=dp[i-1][0]+table[a[i]][5]; 3 4 for(int i=1;i <= n;++i) 5 dp[0][i]=dp[0][i-1]+table[b[i]][5];这一操作是什么意思呢?
根据dp[][]的含义可知,dp[ i ][0]表示的是串 1 匹配到 i 字符,串2匹配到0 的最大相似度;
串2匹配到 0 字符意味着串1的第 i 个字符匹配' - '吗,对应着table表中的table[a[ i ]][5];
而前 i-1 个字符匹配的也是' - ',根据最大相似度的定义,所以匹配到 i 字符处的最大相似度就是
table[a[0]][5]+table[a[1]][5]+........+table[a[ i ]][5],此处用前缀和表示;
同理可得dp[ 0 ][ i ]得含义。
(2):状态转移方程
对于串1的 i 位置,串2的 j 位置时,有一下三种可能:
①:串1的 i 位置和 ' - ' 配对 dp[ i-1 ][ j ]+table[ a[i] ][5];
②:串2的 j 位置和 ' - ' 配对 dp[ i ][ j-1 ]+table[ b[j] ][5];
③:串1的 i 位置和串2的 j 位置配对 dp[ i-1 ][ j-1 ]+table[ a[i] ][ b[j] ];
从中找出最大的相似度赋值给dp[ i ][ j ];
•Code
View Code1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int maxn=100+50; 5 6 int m,n; 7 int dp[maxn][maxn]; 8 int a[maxn],b[maxn]; 9 int table[5][6]={ 10 {0}, 11 {0,5,-1,-2,-1,-3}, 12 {0,-1,5,-3,-2,-4}, 13 {0,-2,-3,5,-2,-2}, 14 {0,-1,-2,-2,5,-1} 15 }; 16 void Solve() 17 { 18 for(int i=1;i <= m;++i) 19 dp[i][0]=dp[i-1][0]+table[a[i]][5]; 20 for(int i=1;i <= n;++i) 21 dp[0][i]=dp[0][i-1]+table[b[i]][5]; 22 for(int i=1;i <= m;++i) 23 { 24 for(int j=1;j <= n;++j) 25 { 26 dp[i][j]=dp[i-1][j]+table[a[i]][5];// 串1的 i 字符匹配 ' _ ' 27 dp[i][j]=max(max(dp[i][j],dp[i][j-1]+table[b[j]][5]),dp[i-1][j-1]+table[a[i]][b[j]]);//串2的j字符匹配'_' 和 串1的i字符匹配串2的j字符 28 } 29 } 30 printf("%d ",dp[m][n]); 31 } 32 int main() 33 { 34 scanf("%d",&m); 35 getchar(); 36 for(int i=1;i <= m;++i) 37 { 38 char letter=getchar(); 39 if(letter == 'A') 40 a[i]=1; 41 else if(letter == 'C') 42 a[i]=2; 43 else if(letter == 'G') 44 a[i]=3; 45 else 46 a[i]=4; 47 } 48 scanf("%d",&n); 49 getchar(); 50 for(int i=1;i <= n;++i) 51 { 52 char letter=getchar(); 53 if(letter == 'A') 54 b[i]=1; 55 else if(letter == 'C') 56 b[i]=2; 57 else if(letter == 'G') 58 b[i]=3; 59 else 60 b[i]=4; 61 } 62 Solve(); 63 }
•初始疑惑
状态转移方程对应的三种状态没有表示处串 1 的 i 字符匹配 ' - '和串2的 j 字符匹配 ' - ' 这一情况啊。
•我的理解
对于情况① dp[ i-1 ][ j ]+table[ a[i] ][5]:
dp[ i-1 ][ j ]的意思是, i 之前的字符和串 2 匹配过程中所能获得的最大的相似度,
如果串1的 i 字符匹配 ' - '和串2的 j 字符匹配 ' - ' 这一情况对应的相似度最大,那么 dp[ i-1 ][ j ]就是串2的 j 字符匹配 ' - '。
情况②同理。
•分析:此题为什么可以用DP来做?
1.满足最优子结构性质
当串1来到 i 位置,串2来到 j 位置,如果dp[ i ][ j ]对应的解是最优解,则其之前的位置dp[1.....i-1][1........j-1]也一定是最优解。
2.无后效性性质
当前状态值dp[ i ][ j ]一旦确定,则此后过程的匹配就只和dp[ i ][ j ]这个状态的值有关,和之前是采取哪种匹配方式演变到当前的状态没有关系。
分割线:2019.6.11
•简短题解
最近在练习动规,拿出之前做的题重新做一下;
定义dp[ i ][ j ]表示串 s 的 1~i 与串 t 的 1~j 的最大匹配度;
dp[ i ][ j ]=max{ dp[ i-1 ][ j-1 ]+|si -- tj| , dp[ i-1 ][ j ]+|si -- ' - '| , dp[ i ][ j-1 ]+|tj -- ' - '| | 1 ≤ j ≤ |t| };
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define MAX(a,b,c) max(max(a,b),c) 4 #define mem(a,b) memset(a,b,sizeof(a)) 5 const int maxn=100+50; 6 7 int n,m; 8 char s[maxn]; 9 char t[maxn]; 10 map<char ,int >f; 11 int g[6][6]= 12 { 13 {5,-1,-2,-1,-3}, 14 {-1,5,-3,-2,-4}, 15 {-2,-3,5,-2,-2}, 16 {-1,-2,-2,5,-1}, 17 {-3,-4,-2,-1} 18 }; 19 20 int dp[maxn][maxn]; 21 int Solve() 22 { 23 f['A']=0,f['C']=1,f['G']=2,f['T']=3; 24 25 mem(dp,0); 26 dp[1][0]=g[f[s[1]]][4]; 27 for(int i=2;i <= n;++i) 28 dp[i][0]=dp[i-1][0]+g[f[s[i]]][4]; 29 30 dp[0][1]=g[f[t[1]]][4]; 31 for(int j=2;j <= m;++j) 32 dp[0][j]=dp[0][j-1]+g[f[t[j]]][4]; 33 34 for(int i=1;i <= n;++i) 35 { 36 for(int j=1;j <= m;++j) 37 { 38 int x=f[s[i]]; 39 int y=f[t[j]]; 40 dp[i][j]=dp[i-1][j-1]+g[x][y]; 41 dp[i][j]=MAX(dp[i][j],dp[i-1][j]+g[x][4],dp[i][j-1]+g[y][4]); 42 } 43 } 44 return dp[n][m]; 45 } 46 int main() 47 { 48 // freopen("C:\Users\hyacinthLJP\Desktop\in&&out\contest","r",stdin); 49 scanf("%d%s",&n,s+1); 50 scanf("%d%s",&m,t+1); 51 printf("%d ",Solve()); 52 53 return 0; 54 }