Luogu P4059 找爸爸

找爸爸

题目描述#

小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,意义如题中所述。

输出格式#

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

样例 #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<A1000,1000d(x,y)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 @   ccz9729  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩