『线性DP』P1140 相似基因

洛谷传送:https://www.luogu.org/problemnew/show/P1140

题意简述:给两个碱基序列A和B和一个碱基匹配的相似度表(权值表),找出两个序列匹配的相似度(最大权值和)。

这一题在洛谷是普及的难度,但困扰了很久,有点难受。

既然是DP,按照套路来分析一波:

一、确定状态

      求什么就设什么,确定状态dp[i][j]为 当A中的前i个元素和空字符组成的序列和B中前j个元素和空字符组成的序列可以匹配的最大权值和。

      从这里会隐约感到和最长LCS的状态定义是十分相似的。

二、刻画最优子结构

     对于序列A和B,采用和LCS一样的分析方式。每次比较最后的两个元素。

     有序列A:AGTGATG 和序列B:GTTAG,i 和 j 分别是当前指向的元素位置。

    从题目可以看出,可以在A和B的序列中加入空碱基,所以每次最后一位元素比较的时候会出现以下的三种情况:

             Ⅰ. A添加空碱基,B不加;

            

             Ⅱ. B添加空碱基,A不加;

           

            Ⅲ. A,B都不添加空碱基。

           

      这样就可以得到这时最优子结构,两个序列的最大权值和就是其去掉最后一个元素的子序列的最大权值和 最后一个元素匹配值。

三、状态转移方程

      从题目可以看出,可以在A和B的序列中加入空碱基,所以每次最后一位元素比较的时候会出现以下的三种情况:

     

     所以我们可以写出状态转移方程

    

四、一个递归基

        考虑两个序列第一个元素和空碱基的配对问题


    for(int i=1;i<=la;i++)
    {
        dp[i][0]=dp[i-1][0]+table[a[i]]['-'];
    }
    for(int i=1;i<=lb;i++)
    {
        dp[0][i]=dp[0][i-1]+table['-'][b[i]];
    }

『代码』

//下标从0开始
#include <algorithm>
#include <bits/stdc++.h>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
int n, m, p = 0;
string a, b;
int dp[205][205];
int main() {
	int table_v[5][5] = {
		5, -1, -2, -1, -3,
		-1, 5, -3, -2, -4,
		-2, -3, 5, -2, -2,
		-1, -2, -2, 5, -1,
		-3, -4, -2, -1, 0
	};
	int num[200];
	string str = "ACGT-";
	for (int i = 0; i < 5; i++) {
		num[str[i]] = i;
	}
	cin >> n >> a >> m >> b;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			dp[i + 1][j + 1] = -2e8;
		}
	}
	for (int i = 0; i < n; i++) {
		dp[i + 1][0] = dp[i][0] + table_v[num[a[i]]][4];
	}
	for (int i = 0; i < m; i++) {
		dp[0][i + 1] = dp[0][i] + table_v[4][num[b[i]]];
	}
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			//a添加空碱基,b不加
			dp[i + 1][j + 1] = max(dp[i + 1][j + 1], dp[i + 1][j] + table_v[4][num[b[j]]]);
			//b添加空碱基,a不加
			dp[i + 1][j + 1] = max(dp[i + 1][j + 1], dp[i][j + 1] + table_v[num[a[i]]][4]);
			//a,b都不添加空碱基
			dp[i + 1][j + 1] = max(dp[i + 1][j + 1], dp[i][j] + table_v[num[a[i]]][num[b[j]]]);
		}
	}
	cout << dp[n][m];
	return 0;
}

           

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值