题目描述
小 A 最近一直在找自己的爸爸,用什么办法呢,就是 DNA 比对。
小 A 有一套自己的 DNA 序列比较方法,其最终目标是最大化两个 DNA 序列的相似程度,具体步骤如下:
1. 给出两个 DNA 序列,第一个长度为n,第二个长度为m
2. 在两个序列的任意位置插入任意多的空格,使得两个字符串长度相同。
3. 逐位进行匹配,如果两个序列相同位置上的字符都不是空格,假设第一个是x,第二个是y,那么他们的相似程度由d(x,y)定义。对于两个序列中任意一段极长的长度为k的连续空格,我们定义这段空格的相似程度为g(k)=AB(k1)
那么最终两个序列的相似程度就是所有的d(x,y)加上所有的极长空格段的相似程度之和。
现在小 A 通过某种奥妙重重的方式得到了小 B 的 DNA 序列中的一段,他想请你帮他算一下小 A 的 DNA 序列和小 B 的 DNA 序列的最大相似程度。

输入格式
输入第1一个字符串,表示小 A 的 DNA 序列。
输入第2行一个字符串,表示小 B 的 DNA 序列。
接下来4行,每行4个整数,用空格隔开,表示d数组,具体顺序如下所示。
d(A,A) d(A,T) d(A,G) d(A,C)
d(T,A) d(T,T) d(T,G) d(T,C)
d(G,A) d(G,T) d(G,G) d(G,C)
d(C,A) d(C,T) d(C,G) d(C,C)
最后一行两个用空格隔开的正整数A,B,意义如题中所述。

输出格式
输出共一行,表示两个序列的最大相似程度。

样例输入
ATGG
ATCC
5 -4 -4 -4
-4 5 -4 -4
-4 -4 5 -4
-4 -4 -4 5
2 1

样例输出
4

样例解释
首先,将序列补成如下形式(-代表空格)
A T G G - -
A T - - C C
然后所有d(x,y)的和为d(A,A)+d(T,T)=10,所有极长连续空格段的相似程度之和为g(2)+g(2)=6。总和为4,可以验证,这是相似程度最大的情况。

数据范围与提示
对于所有测试点,有0<B<A1000,1000d(x,y)1000,d(x,y)=d(y,x),序列只包含A,T,G,C四种字符。

测试点编号 n+m的范围 特殊约定
1 n=m=1 无特殊要求
2 n+m15 无特殊要求
3 n+m300 无特殊要求
4 n+m300 无特殊要求
5 n+m3000 序列中只包含一种字符
6 n+m3000 无特殊要求
7 n+m3000 无特殊要求
8 n+m3000 无特殊要求
9 n+m3000 无特殊要求
10 n+m3000 无特殊要求

来自 CodePlus 2017 11 月赛,清华大学计算机科学与技术系学生算法与竞赛协会 荣誉出品。
Credit:idea/邢健开 命题/邢健开 验题/陈宇
Git Repo:https://git.thusaac.org/publish/CodePlus201711
感谢腾讯公司对此次比赛的支持。

思路
直接dp,方程太长,看代码好了。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>

const int maxn=3000;
const int inf=1000000000;

int f[maxn+10][maxn+10][2][2];
//f[i][j][k][l]代表A序列匹配到了i位,B序列匹配到了j位,k代表A序列末尾是否有空格,l代表B序列是否有空格
char s[maxn+10],t[maxn+10];
int a[maxn+10],b[maxn+10],w[4][4],lena,lenb,x,y;

int main()
{
  scanf("%s%s",s+1,t+1);
  lena=strlen(s+1);
  lenb=strlen(t+1);
  for(register int i=1; i<=lena; ++i)
    {
      if(s[i]=='A')
        {
          a[i]=0;
        }
      else if(s[i]=='T')
        {
          a[i]=1;
        }
      else if(s[i]=='G')
        {
          a[i]=2;
        }
      else if(s[i]=='C')
        {
          a[i]=3;
        }
    }
  for(register int i=1; i<=lenb; ++i)
    {
      if(t[i]=='A')
        {
          b[i]=0;
        }
      else if(t[i]=='T')
        {
          b[i]=1;
        }
      else if(t[i]=='G')
        {
          b[i]=2;
        }
      else if(t[i]=='C')
        {
          b[i]=3;
        }
    }
  for(register int i=0; i<4; ++i)
    {
      for(register int j=0; j<4; ++j)
        {
          scanf("%d",&w[i][j]);
        }
    }
  scanf("%d%d",&x,&y);
  f[0][0][0][0]=0;
  f[0][0][0][1]=-inf;
  f[0][0][1][0]=-inf;
  for(register int i=1; i<=lena; ++i)
    {
      f[i][0][0][1]=-x-y*(i-1);
      f[i][0][1][0]=f[i][0][0][0]=-inf;
    }
  for(register int i=1; i<=lenb; ++i)
    {
      f[0][i][1][0]=-x-y*(i-1);
      f[0][i][0][1]=f[0][i][0][0]=-inf;
    }
  //上面赋初值,下面dp
  for(register int i=1; i<=lena; ++i)
    {
      for(register int j=1; j<=lenb; ++j)
        {
          f[i][j][0][0]=std::max(f[i-1][j-1][0][0],std::max(f[i-1][j-1][0][1],f[i-1][j-1][1][0]))+w[a[i]][b[j]];
          f[i][j][0][1]=std::max(f[i-1][j][0][0]-x,std::max(f[i-1][j][1][0]-x,f[i-1][j][0][1]-y));
          f[i][j][1][0]=std::max(f[i][j-1][0][0]-x,std::max(f[i][j-1][0][1]-x,f[i][j-1][1][0]-y));
        }
    }
  printf("%d\n",std::max(f[lena][lenb][0][0],std::max(f[lena][lenb][1][0],f[lena][lenb][0][1])));
  return 0;
}