D. 相似基因 - 2023HBUCM程序设计竞赛

题面

p哥作为一名湖中医信息工程学院的同学,不仅对信息有兴趣,同时对生物也很有兴趣。相信大家从初高中生生物基本知识都知道,DNA基因可以看作一个碱基对序列。它包含了 4 种核苷酸,简记作 A,C,G,T
现在假设想计算两个基因的相似程度,相似度的计算方法如下:
对于两个已知基因,例如AGTGATGGTTAG,将它们的碱基互相对应。当然,中间可以加入一些空碱基-,例如:

A G T G A T - G
- G T - - T A G

这样,两个基因之间的相似度就可以用碱基之间相似度的总和来描述,碱基之间的相似度如下表所示:

A C G T -
A 5 -1 -2 -1 -3 A
C -1 5 -3 -2 -4 C
G -2 -3 5 -2 -2 G
T -1 -2 -2 5 -1 T
- -3 -4 -2 -1 * -

那么相似度就是:(3)+5+5+(2)+(3)+5+(3)+5=9 。但中间添加空碱基的方式不一定,因此两个基因的对应方法不唯一,例如又有:

A G T G A T G
- G T T A - G

相似度为:(3)+5+5+(2)+5+(1)+5=14
规定两个基因的相似度为所有对应方法中,取相似度最大的那个。

输入:共两行。每行首先是一个整数,表示基因的长度;隔一个空格后是一个基因序列,序列中只含 A,C,G,T 四个字母。1<= 序列的长度 <=100
输出:仅一行,即输出基因的相似度。

题解

什么时候考虑DP:最优子结构、重叠子问题。

  • 最优子结构: 问题的最优解可以通过子问题的最优解来构建。
    在这个问题中,两个基因序列的最优匹配可以通过子问题(较短的基因序列的匹配)的最优匹配来构建。
  • 重叠子问题: 在解决问题的过程中,会多次遇到相同的子问题。
    在这个问题中,计算两个基因序列的相似度时,可能会多次计算相同子序列的相似度。通过动态规划,可以将中间结果存储起来,避免重复计算,提高效率。

复习一下闫式dp分析法:AcWing 2. 01背包问题
集合a 串的前 i 个碱基 + b 串的前 j 个碱基;
属性:要求最大相似度,即为最大价值问题。
划分方案:对于任意一对碱基,有且仅有以下三种情况:

  • 非空碱基和非空碱基
  • 非空碱基和空碱基
  • 空碱基和非空碱基

本题状态转移的关键点只需要考虑最后一对碱基,逐步缩小问题规模。
状态转移方程fi,j=max(fi1,j1+dai,bj,fi1,j+dai,,fi,j1+dbj,)
其中 d 表示两个碱基之间的相似程度。
边界问题fi,0=fi1,0+dai,f0,j=f0,j1+dbi,
考虑问题规模变小到极限的情况,此处为碱基串首/尾未对齐,与空串匹配的情况。

#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int n, m, f[N][N];
string a, b;
//建立相似度对应关系,也可以使用矩阵
int sim(char a, char b) {
if (a == b) return 5;
if ((a == 'A' && b == 'C') || (a == 'C' && b == 'A')) return -1;
if ((a == 'A' && b == 'T') || (a == 'T' && b == 'A')) return -1;
if ((a == 'A' && b == 'G') || (a == 'G' && b == 'A')) return -2;
if ((a == 'C' && b == 'G') || (a == 'G' && b == 'C')) return -3;
if ((a == 'C' && b == 'T') || (a == 'T' && b == 'C')) return -2;
if ((a == 'T' && b == 'G') || (a == 'G' && b == 'T')) return -2;
if (a == 'A' && b == '0') return -3;
if (a == 'C' && b == '0') return -4;
if (a == 'G' && b == '0') return -2;
if (a == 'T' && b == '0') return -1;
return -100;
}
int main()
{
cin >> n >> a >> m >> b;
//优先处理边界问题
for (int i = 1; i <= n; i++)
f[i][0] = f[i - 1][0] + sim(a[i - 1], '0');
for (int i = 1; i <= m; i++)
f[0][i] = f[0][i - 1] + sim(b[i - 1], '0');
//状态转移方程
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
f[i][j] = max({ f[i - 1][j - 1] + sim(a[i - 1], b[j - 1]), f[i - 1][j] + sim(a[i - 1], '0'), f[i][j - 1] + sim(b[j - 1], '0') });
cout << f[n][m];
}

蒟蒻有(fei)话说:非常贴近实际的一道题,虽然当场没能出(该打)但是看到还是忍俊不禁了
当时第一反应想到的是一些字符串匹配问题,完全没往dp那方面想,题做的实在太少了
碎碎念:现在对于科研相关的一些有限的理解感觉就是,如何用有限的数据把黑的吹成白的……

posted @   蒟蒻爬行中  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示