zoukankan      html  css  js  c++  java
  • 洛谷 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
  • 相关阅读:
    2020.10.13辗转相除法
    关于……
    友情链接
    李群笔记
    c++中的复数
    python快速傅里叶变换
    多参数函数共轭梯度法寻找极值:gsl_multimin_fdfminimizer
    python: matplotlib.pyplot 制作动图
    简易威尔逊云室计划
    大规模矩阵对角化方法:Lanczos
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/9878937.html
Copyright © 2011-2022 走看看