洛谷 P1140 相似基因(DP)

传送门

参考资料

  [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

 1 #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 }
View Code

初始疑惑

  状态转移方程对应的三种状态没有表示处串 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 ]+|s-- tj| , dp[ i-1 ][ j ]+|s-- ' - '| , dp[ i ][ j-1 ]+|t-- ' - '|  | 1 ≤ j ≤ |t| };

Code

 1 #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 }
View Code
原文地址:https://www.cnblogs.com/violet-acmer/p/9878937.html