题目大意:每次给出两个碱基序列(包含ATGC的两个字符串),其中每一个碱基与另一串中碱基如果配对或者与空串对应会有一个分数(可能为负),找出一种方式使得两个序列配对的分数最大
思路:字符串动态规划的经典题,很容易想到状态dp[i][j],指第一个长度为i的串和第二个长度为j的串配对的最大分数。显然,这个状态可以由dp[i][j-1],dp[i-1][j],dp[i-1][j-1]三个子问题得到,即第一串最后一个字符对应空格、第二串最后一个字符对应空格和第一串第二串最后一个字符配对所得到的分数这三者的最大值。
方程:dp[i][j]=max{dp[i-1][j]+grade[i][grade],dp[i][j-1]+grade[space][j],dp[i-1][j-1]+grade[i][j]}
grade[i][j]指的是字符串1的第i个碱基和字符串2的第j个碱基配对的分数 space指配对空格的分数
code:
#include<cstdio>
#include<string.h>
#include<iostream>
using namespace std;
const int trans[10][10]=
{{0},{0,5,-1,-2,-1,-3},
{0,-1,5,-3,-2,-4},
{0,-2,-3,5,-2,-2},
{0,-1,-2,-2,5,-1},
{0,-3,-4,-2,-1,-19941117}//我竟然和杜宇飞神犇生日同一天!!以后无穷大就用它了
};
int tr(char ch)
{
if (ch=='A')return 1;if (ch=='C')return 2;
if (ch=='G')return 3;if (ch=='T')return 4;
return 0;
}
int max(int a,int b,int c)
{
if (a<b)a=b;
if (a<c)a=c;
return a;
}
int main()
{
int t,gene1[200]={0},gene2[200]={0},n1,n2,dp[200][200]={{0}};
scanf("%d",&t);
char ch1[200],ch2[200];
for(int k=1;k<=t;k++)
{
memset(dp,0,sizeof(dp));
scanf("%d",&n1);
scanf("%s",ch1);
for(int i=1;i<=n1;i++)
{
gene1[i]=tr(ch1[i-1]);
}
scanf("%d",&n2);
scanf("%s",ch2);
for(int i=1;i<=n2;i++)
{
gene2[i]=tr(ch2[i-1]);
}
for(int i=1;i<=n1;i++)dp[i][0]=dp[i-1][0]+trans[gene1[i]][5];
for(int i=1;i<=n2;i++)dp[0][i]=dp[0][i-1]+trans[gene2[i]][5];
for(int i=1;i<=n1;i++)
{
for(int j=1;j<=n2;j++)
{
dp[i][j]=max(dp[i-1][j]+trans[gene1[i]][5],
dp[i][j-1]+trans[gene2[j]][5],
dp[i-1][j-1]+trans[gene1[i]][gene2[j]]);
}
}
printf("%d\n",dp[n1][n2]);
}
return 0;
}
调试结果:2WA 原因:一开始看discuss里别人犯错的原因:矩阵抄错、dp方程写错、数组写小了.....一个个对下来好像都没错,从头到尾看了两遍才发现dp的初始条件写错了,这样竟然都能过样例太坑了!!看来以后自己构造边缘数据测试程序鲁棒性的能力得加强!!
【BTW】貌似比较正规的解题报告就是从这篇开始的(正规是指题目大意,算法,code以及附加)