题解:P10956 金字塔

· · 题解

区间 dp 怎么就只有四十道题啊。

思路

一棵树可以从他的子树转移得到,果断选择区间 dp(也有树形 dp 的思想)。每从根节点出发,遍历完一棵子树,就要重新返回根节点。所以字符串应该长这样:

根 子树1 根 子树2... 子树n 根

那么就可以区间 [i,j] 就可以由区间 [i,k][k+1,j-1] 转移得到([i,k] 是本来的子树,[k+1,j-1] 是他新的一颗子树)。

Code

#include<bits/stdc++.h>
#define int long long 
using namespace std;
string a;
int dp[305][305];
const int mod=1e9;
signed main()
{
    cin>>a;
    int n=a.size();
    a=" "+a;
    for(int i=1;i<=n;i++)
    {
        dp[i][i]=1;
    }
    for(int len=2;len<=n;len++)
    {
        for(int i=1;i+len-1<=n;i++)
        {
            int j=i+len-1;
            if(a[i]==a[j])
            {
                for(int k=i+1;k<j;k++)
                {
                    if(a[k]==a[i])
                    {
                        dp[i][j]+=dp[i][k]*dp[k+1][j-1];
                        dp[i][j]%=mod;
                    }
                }
                dp[i][j]+=dp[i+1][j-1];
                dp[i][j]%=mod;
            }
        }
    }
    cout<<dp[1][n]<<'\n';
    return 0;
}

但你写出来之后,发现跑了 136ms,实在是太慢了。不难发现,只有在长度为奇数时才可能有答案。所以长度 len 的遍历可以改成这样:

for(int len=3;len<=n;len+=2)

时间复杂度十分优秀,快了整整 12ms