LuoguP4059 找爸爸-序列DP
容我吐槽一波题目名字。。
首先我们考虑如果没有空格连续产生代价这一额外条件,那么是不是很好做。
因为我们注意到,不管怎么放空格,我们最终都是把两个串匹配完了。
同时,同一位置上下都放空格是完全没有意义的。
所以我们直接设f[i][j]表示第一个匹配到了i,第二个匹配到了j。
那么转移就是f[i][j]=max {f[i][j-1], f[j][i-1], f[i-1][j-1]+d[i][j]}。
然而这里多了一个条件。。那该怎么做呢??
同样的,我们仍然容易发现,同一位置上下都放空格不会是结果更优。
那么对于我们就可以把这个求额外贡献的式子拆成:
第一个0就减a,以后的连起来的0都减b。
在此基础上,我们考虑设f[i][j][0/1/2]表示第一个匹配到了i,第二个匹配到了j,且此时末尾(没有/第一个串/第二个串)出现了0。
那么的话,转移就很简单了:
f[i][j][0]=max (f[i-1][j-1][0], max (f[i-1][j-1][1], f[i-1][j-1][2]))+d[ad[i]][bd[j]];
f[i][j][1]=max (f[i][j-1][0], f[i][j-1][2])-a;
f[i][j][1]=max (f[i][j][1], f[i][j-1][1]-b);
f[i][j][2]=max (f[i-1][j][0], f[i-1][j][1])-a;
f[i][j][2]=max (f[i][j][2], f[i-1][j][2]-b);
最后稍微注意下边界就好了。。
#include <bits/stdc++.h>
using namespace std;
inline int gi () {
int x=0,w=0; char ch=0;
while (!(ch>='0' && ch<='9')) {if (ch=='-') w=1; ch=getchar ();}
while (ch>='0' && ch<='9') x= (x<<3)+(x<<1)+(ch^48), ch=getchar ();
return w?-x:x;
}
#define RG register
const int N=3010;
char ADNA[N],BDNA[N];
int a,b,alen,blen,ad[N],bd[N],d[5][5],f[N][N][3];
int main ()
{
RG int i,j;
scanf ("%s%s", ADNA+1, BDNA+1);
for (i=1;i<=4;++i)
for (j=1;j<=4;++j)
d[i][j]=gi ();
a=gi (), b=gi ();
alen=strlen (ADNA+1), blen=strlen (BDNA+1);
for (i=1;i<=alen;++i)
switch (ADNA[i]) {
case 'A': ad[i]=1; break;
case 'T': ad[i]=2; break;
case 'G': ad[i]=3; break;
case 'C': ad[i]=4; break;
}
for (i=1;i<=blen;++i)
switch (BDNA[i]) {
case 'A': bd[i]=1; break;
case 'T': bd[i]=2; break;
case 'G': bd[i]=3; break;
case 'C': bd[i]=4; break;
}
memset (f, 0xcf, sizeof (f));
f[0][0][0]=0, f[0][1][1]=f[1][0][2]=-a;
for (i=1;i<=alen;++i)
for (j=1;j<=blen;++j) {
f[i][j][0]=max (f[i-1][j-1][0], max (f[i-1][j-1][1], f[i-1][j-1][2]))+d[ad[i]][bd[j]];
f[i][j][1]=max (f[i][j-1][0], f[i][j-1][2])-a;
f[i][j][1]=max (f[i][j][1], f[i][j-1][1]-b);
f[i][j][2]=max (f[i-1][j][0], f[i-1][j][1])-a;
f[i][j][2]=max (f[i][j][2], f[i-1][j][2]-b);
}
printf ("%d\n", max (f[alen][blen][0], max (f[alen][blen][1], f[alen][blen][2])));
return 0;
}