Luogu P4059 找爸爸

找爸爸

题目描述

小A最近一直在找自己的爸爸,用什么办法呢,就是DNA比对。

小A有一套自己的DNA序列比较方法,其最终目标是最大化两个DNA序列的相似程度,具体步骤如下:

  1. 给出两个DNA序列,第一个长度为\(n\),第二个长度为\(m\)

  2. 在两个序列的任意位置插入任意多的空格,使得两个字符串长度相同

  3. 逐位进行匹配,如果两个序列相同位置上的字符都不是空格,假设第一个是\(x\),第二个是\(y\),那么他们的相似程度由\(d(x,y)\)定义。对于两个序列中任意一段极长的长度为\(k\)的连续空格,我们定义这段空格的相似程度为\(g(k)=-A-B(k-1)\)

那么最终两个序列的相似程度就是所有的\(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\),意义如题中所述。

输出格式

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

样例 #1

样例输入 #1

ATGG
ATCC
5 -4 -4 -4 
-4 5 -4 -4 
-4 -4 5 -4 
-4 -4 -4 5 
2 1

样例输出 #1

4

提示

样例解释

首先,将序列补成如下形式("-"代表空格)

ATGG--
AT--CC

然后所有\(d(x,y)\)的和为\(d(A,A)+d(T,T)=10\)

所有极长连续空格段的相似程度之和为\(g(2)+g(2)=-6\)

总和为\(4\),可以验证,这是相似程度最大的情况。

对于所有测试点,有\(0< B<A \le 1000, -1000\le d(x,y)\le 1000,d(x,y)=d(y,x)\),序列只包含\({A,T,G,C}\)四种字符。

SOLUTION

首先我们考虑每个位置上的字符:

  • 如果两个都为字符,那么它的贡献是给定的一个值
  • 如果一个是空格一个是字符,那么他的贡献需要考虑他是的前一个是否也是空格
    • 如果是第一个空格,则贡献为 \(-a\)
    • 如果不是第一个空格,则贡献为 \(-b\)
  • 如果连个都为空格,那么他的贡献不会增加,因此我们舍弃这种情况。

因此我们对于某个位置,只需要记录它的前一个位置的情况即可。
可以定义 \(f[i][j][0/1/2]\) 分别表示 \(A\) 使用了 \(i\) 个字符 \(B\) 使用了 \(j\) 个字符 并且最后一对字符为:

  • 0 均为字符
  • 1 \(A\)为空格,\(B\)为字符
  • 2 \(B\)为空格,\(A\)为字符

然后进行状态转移即可

CODE

#include <bits/stdc++.h>
using namespace std;

#define rep(i, b, s) for(int i = (b); i <= (s); ++ i)
#define dec(i, b, s) for(int i = (b); i >= (s); -- i)
#define YES cout << "YES" << "\n"; return  
#define NO  cout << "NO" << "\n";  return  

using ll = long long;

#ifdef LOCAL
#include <debugger>
#else
#define debug(...) 42
#endif

template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }


constexpr ll inf = 1e18;

map<pair<char, char>, int> mp;

void solve() {
  string s1, s2; cin >> s1 >> s2;
  int n = s1.size(), m = s2.size();
  s1 = " " + s1;
  s2 = " " + s2;
  vector<char> all{'A', 'T', 'G', 'C'};
  for(int i = 0; i < 4; i ++ ) {
    for(int j = 0; j < 4; j ++ ) {
      int x; cin >> x;
      mp[{all[i], all[j]}] = x;
    }
  }
  // cout << mp[{'G', 'C'}] << "\n";
  ll x, y; cin >> x >> y;
  vector<vector<array<ll, 3> > > f(n + 1, vector<array<ll, 3> >(m + 1, {-inf, -inf, -inf}));

  for(int i = 1; i <= n || i <= m; ++ i ) {
    if(i <= m) {
      f[0][i][1] = -x - y * (i - 1);
    } 
    if(i <= n) {
      f[i][0][2] = -x - y * (i - 1);
    }
  } 

  f[0][0][0] = 0;
  //f[i][j][0] 表示s1匹配了i个, s2匹配了j个,并且当前状态为两个字符
  //f[i][j][1] 表示s1匹配了i个, s2匹配了j个,并且当前状态为s1是空格,s2是字符
  //f[i][j][2] 表示s1匹配了i个, s2匹配了j个,并且当前状态为s1是字符,s2是空格
  for (int i = 1; i <= n; i ++ ) {
    for (int j = 1; j <= m; j ++ ) {
      //
      //
      f[i][j][0] = max({f[i - 1][j - 1][0], f[i - 1][j - 1][1], f[i - 1][j - 1][2]}) + mp[{s1[i], s2[j]}];

      f[i][j][1] = max({f[i][j - 1][0] - x, f[i][j - 1][1] - y, f[i][j - 1][2] - x});

      f[i][j][2] = max({f[i - 1][j][0] - x, f[i - 1][j][1] - x, f[i - 1][j][2] - y}); 
      // f[i][j][1] = max({f[i - 1][j][0], f[i - 1][j][2]});
      // f[i][j][2] = max({f[i][j - 1][0], f[i][j - 1][1]});
      // printf("%d %d -> %lld %lld %lld\n", i, j, f[i][j][0], f[i][j][1], f[i][j][2]);
      // printf("f[%d][%d][%d] = %lld\n", i, j, ) 
    }
  }
  // cout << f[1][1][1] << "\n";
  cout << max({f[n][m][0], f[n][m][1], f[n][m][2]}) << "\n";
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int T = 1; //cin >> T;
  while(T -- ) solve();
  return 0;
}
posted @ 2022-06-25 17:26  ccz9729  阅读(28)  评论(0编辑  收藏  举报