总之就是 | 洛谷 P4059 找爸爸
P4059 [ Code+#1 ] 找爸爸
P4059 找爸爸 [提高+/省选-]
算是一道比较好想的线性 DP。
题意简述
给你两个字符串,求它们的最大相似程度,你可以用添加空格的方法去改变这两个字符串的相似程度。
添加空格后要保证两个字符串长度相等。
相似程度的计算方式如下:
-
若两个字符串的第 \(\texttt{i}\) 位都为字母,那么按照题目给出的 \(\texttt{d}\) 进行计算。
-
否则,设空格的长度为 \(\texttt{k}\),则有计算公式 \(g(k)=-A-B(k-1)\)。
思路简述
看到数据范围之后实际上就可以放心的开三维数组来进行 \(\texttt{DP}\)。
我们对结尾空格的存在情况进行分类:
设 \(f(i,j,p)\) 代表当第一个字符串匹配到第 \(\texttt{i}\) 位,第二个字符串匹配到第 \(\texttt{j}\) 位时的状态,\(\texttt{p}\) 代表结尾空格的归属,有如下几种情况:
-
两个字符串结尾都无空格,\(\texttt{k}=0\)。
-
只有第一个字符串结尾有空格,\(\texttt{k}=1\)。
-
只有第一个字符串结尾有空格,\(\texttt{k}=2\)。
-
两个字符串结尾都有空格,\(\texttt{k}=3\)。
分类之后,先别急着去想转移方程,我们先来看看这四种情况是否都能够给我们带来最优解。
实际上判断依据就是去看这四种情况在转移状态时能不能产生更优的解。
前三种的正确性是显然的,手模一下就可以出来,这里就不再详细阐述,在这里主要来看一下第四种,即两个字符串结尾都有空格的情况。
我们考虑到题目要求的是填补完空格之后两个字符串的长度一样。
也就是说,同时去掉这两个空格后同样能够满足题目的要求。
于是相比这种更少空格的情况,两个字符串结尾都有空格的时候我们要多算一个 \(g(k)\),由于空格的长度 \(\texttt{k}\) 一定是大于 \(0\) 的,所以我们只需要考虑 \(A\) 和 \(B\) 的正负。
注意到,题目数据范围是 \(0< B < A \le 1000\),也就是说 \(A\) 和 \(B\) 恒大于 \(0\),于是 \(g(k)=-A-B(k-1)\) 就恒小于 \(0\)。
于是我们可以发现上面所述的第四种情况是不能给我们带来更优的方案的,所以我们可以直接排除掉这种情况。
少了一种情况之后,状态转移方程就变的非常清晰。因为我们设计的是三维的方程,所以我们只需要利用相应情况的方程进行转移即可:
最后我们的答案显然是 \(ans=\max\{f(n,m,0),f(n,m,1),f(n,m,2)\}\)
但是先别急着去写,我们还要考虑边界条件,因为两个字符串是等价的,所以我们只需要考虑一个即可,在下面我就以第一个为例。
实际上考虑我们设计的状态的话,比较好得出其中一组边界状态应当是 \(f(i,0,p)\ ,i\in[1,n]\ ,p\in\{0,1,2\}\),其中我们把 \(p=0\) 和 \(p=1\) 两种情况赋值负无穷,\(p=2\) 时则将其赋值为 \(-A-B(i-1)\)。
另一组就是 \(f(0,0,1)\) 和 \(f(0,0,2)\),它们的初值都是负无穷。
然后我们的思路就讲完了,上代码罢。
Code
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <string.h>
#define Heriko return
#define Deltana 0
#define S signed
#define LL long long
#define R register
#define I inline
#define CI const int
#define mst(a, b) memset(a, b, sizeof(a))
#define ON std::ios::sync_with_stdio(false)
using namespace std;
template<typename T>
I void fr(T &x)
{
short f=1;
char c=getchar();
x=0;
while(c<'0' or c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while (c>='0' and c<='9')
{
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
}
x*=f;
}
template<typename T>
I void fw(T x,bool k)
{
if(x<0) putchar('-'),x=-x;
static short stak[35];
short top=0;
do
{
stak[top++]=x%10;
x/=10;
}
while(x);
while(top) putchar(stak[--top]+'0');
if(k) putchar('\n');
else putchar(' ');
}
template<typename T>
I T hmax(T x,T y)
{
Heriko x>y?x:y;
}
CI MXX=3010,INF=998244353;
int D[5][5],n,m,a1[MXX],a2[MXX],f[MXX][MXX][3],A,B,ans=-INF;
char s1[MXX],s2[MXX];
I void CtI()
{
for(R int i=1;i<=n;++i)
if(s1[i]=='A') a1[i]=1;
else if(s1[i]=='T') a1[i]=2;
else if(s1[i]=='G') a1[i]=3;
else if(s1[i]=='C') a1[i]=4;
for(R int i=1;i<=m;++i)
if(s2[i]=='A') a2[i]=1;
else if(s2[i]=='T') a2[i]=2;
else if(s2[i]=='G') a2[i]=3;
else if(s2[i]=='C') a2[i]=4;
}
I void Pre()
{
for(R int i=1;i<=n;++i) f[i][0][1]=f[i][0][0]=-INF,f[i][0][2]=-A-B*(i-1);
for(R int i=1;i<=m;++i) f[0][i][1]=f[0][i][0]=-INF,f[0][i][2]=-A-B*(i-1);
f[0][0][1]=f[0][0][2]=-INF;
}
S main()
{
scanf("%s\n%s",s1+1,s2+1);
n=strlen(s1+1);m=strlen(s2+1);
for(R int i=1;i<=4;++i)
for(R int j=1;j<=4;++j)
fr(D[i][j]);
fr(A),fr(B);
CtI();Pre();
for(R int i=1;i<=n;++i)
for(R int j=1;j<=m;++j)
{
f[i][j][0]=hmax(f[i-1][j-1][0],hmax(f[i-1][j-1][1],f[i-1][j-1][2]))+D[a1[i]][a2[j]];
f[i][j][1]=hmax(f[i][j-1][0]-A,hmax(f[i][j-1][1]-B,f[i][j-1][2]-A));
f[i][j][2]=hmax(f[i-1][j][0]-A,hmax(f[i-1][j][1]-A,f[i-1][j][2]-B));
}
ans=hmax(ans,hmax(f[n][m][0],hmax(f[n][m][1],f[n][m][2])));
fw(ans,1);
Heriko Deltana;
}
End
~