【bzoj5107】[CodePlus2017]找爸爸 dp

题目描述

给出两个基因串,你需要在其中插入任意个空格,使得两个串长度相同。如果两个串的某同一位置都是字母则获得某给定收益,对于每个串的每个长度为k的连续空格段要付出a(k-1)+b的损失。求最大净收益。

输入

输入第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,意义如题中所述。
对于所有测试点
有0<B<A≤1000,-1000≤d(x,y)≤1000,d(x,y)=d(y,x),
序列只包含{A,T,G,C}四种字符。
N+M<=3000

输出

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

样例输入

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

样例输出

4


题解

dp

显然空格匹配空格是血亏的,所以一定不会有两个位置都是空格。

于是可以分别设 $f[i][j],g[i][j],h[i][j]$ 表示第一个串匹配到 $i$ ,第二个串匹配到 $j$ ,最后为 空格&字母/字母&空格/字母&字母 的最大收益。

那么直接枚举 $i$ 和 $j$ 转移即可。

注意一下边界问题。

时间复杂度 $O(nm)$ 

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 3010
#define val(c) (c == 'A' ? 0 : c == 'T' ? 1 : c == 'G' ? 2 : 3)
using namespace std;
int v[4][4] , f[N][N] , g[N][N] , h[N][N];
char A[N] , B[N];
int main()
{
	int n , m , i , j , p , q;
	scanf("%s%s" , A + 1 , B + 1) , n = strlen(A + 1) , m = strlen(B + 1);
	for(i = 0 ; i < 4 ; i ++ )
		for(j = 0 ; j < 4 ; j ++ )
			scanf("%d" , &v[i][j]);
	scanf("%d%d" , &p , &q);
	memset(f , 0xc0 , sizeof(f));
	memset(g , 0xc0 , sizeof(g));
	memset(h , 0xc0 , sizeof(h));
	f[0][1] = g[1][0] = -p , h[0][0] = 0;
	for(i = 2 ; i <= m ; i ++ ) f[0][i] = f[0][i - 1] - q;
	for(i = 2 ; i <= n ; i ++ ) g[i][0] = g[i - 1][0] - q;
	for(i = 1 ; i <= n ; i ++ )
	{
		for(j = 1 ; j <= m ; j ++ )
		{
			f[i][j] = max(f[i][j - 1] - q , max(g[i][j - 1] , h[i][j - 1]) - p);
			g[i][j] = max(max(f[i - 1][j] , h[i - 1][j]) - p , g[i - 1][j] - q);
			h[i][j] = max(max(f[i - 1][j - 1] , g[i - 1][j - 1]) , h[i - 1][j - 1]) + v[val(A[i])][val(B[j])];
		}
	}
	printf("%d
" , max(max(f[n][m] , g[n][m]) , h[n][m]));
	return 0;
}

 

原文地址:https://www.cnblogs.com/GXZlegend/p/7992539.html