1. 程式人生 > 其它 >總之就是 | 洛谷 P4059 找爸爸

總之就是 | 洛谷 P4059 找爸爸

P4059 [ Code+#1 ] 找爸爸

P4059 找爸爸 [提高+/省選-]

算是一道比較好想的線性 DP。

題意簡述

給你兩個字串,求它們的最大相似程度,你可以用新增空格的方法去改變這兩個字串的相似程度。

新增空格後要保證兩個字串長度相等。

相似程度的計算方式如下:

  1. 若兩個字串的第 \(\texttt{i}\) 位都為字母,那麼按照題目給出的 \(\texttt{d}\) 進行計算。

  2. 否則,設空格的長度為 \(\texttt{k}\),則有計算公式 \(g(k)=-A-B(k-1)\)

思路簡述

看到資料範圍之後實際上就可以放心的開三維陣列來進行 \(\texttt{DP}\)

我們對結尾空格的存在情況進行分類:

\(f(i,j,p)\) 代表當第一個字串匹配到第 \(\texttt{i}\) 位,第二個字串匹配到第 \(\texttt{j}\) 位時的狀態,\(\texttt{p}\) 代表結尾空格的歸屬,有如下幾種情況:

  1. 兩個字串結尾都無空格\(\texttt{k}=0\)

  2. 只有第一個字串結尾有空格,\(\texttt{k}=1\)

  3. 只有第一個字串結尾有空格,\(\texttt{k}=2\)

  4. 兩個字串結尾都有空格\(\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\)

於是我們可以發現上面所述的第四種情況是不能給我們帶來更優的方案的,所以我們可以直接排除掉這種情況。

少了一種情況之後,狀態轉移方程就變的非常清晰。因為我們設計的是三維的方程,所以我們只需要利用相應情況的方程進行轉移即可:

\[\begin{cases}f(i,j,0)=\max\{f(i-1,j-1,0),f(i-1,j-1,1),f(i-1,j-1,2)\}+d(i,j) \\ f(i,j,1)=\max\{f(i,j-1,0)-A,f(i,j-1,1)-B,f(i,j-1,2)-A\} \\ f(i,j,2)=\max\{f(i-1,j,0)-A,f(i-1,j,1)-A,f(i-1,j,2)-B\}\end{cases} \]

最後我們的答案顯然是 \(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

~