金字塔 题解
Posted 。✧* ꧁王者꧂✧*
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了金字塔 题解相关的知识,希望对你有一定的参考价值。
哒哒哒~
初看本题时,一头雾水,根本不知从何处下手,只隐隐感到,如果将两个相同的字符视为一颗子树的根,应该会有多种可能吧。当然,这种想法也只是一瞬即逝,但却是解锁本题的关键。
于是,就根据这个思路来DP,那么答案应该就是
d
p
[
1
]
[
n
]
dp[1][n]
dp[1][n]了,这是显然的。那么,如何DP呢?可以发现,本题算是道区间DP,且只有当
d
p
[
l
]
[
r
]
dp[l][r]
dp[l][r]中
l
l
l和
r
r
r处的字符相同时,才会有值。所以,从小区间转向大区间时,小区间的两端字符一定是相同的,这是转移的条件。那么,我们怎么样才能不重不漏地并且不多余地计算出
f
[
l
]
[
r
]
f[l][r]
f[l][r]的值呢?这是本题十分关键的一点。
所以,为了能够完美地计算
f
[
l
]
[
r
]
f[l][r]
f[l][r],我们每次转移时,可以枚举当前根连接的第一颗子树的大小(因为是深度优先遍历,第一颗子树大小不同,可以证明这两棵子树在深度优先遍历的意义下一定不同,即使二者形态相同),而找寻最大的子树,便是枚举
l
l
l和
r
r
r中间的一个
k
k
k,使
l
+
1
l+1
l+1和
k
−
1
k-1
k−1处的字符相同且
k
k
k和
r
r
r的字符相同。于是,枚举就好。
总复杂度:
O
(
n
3
)
O(n^3)
O(n3)
本人的写法是
d
f
s
dfs
dfs,但依旧是这个思路(参考《算法进阶》)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
char s[350];
int M=1e9;
long long f[310][310];//f[i][j]指遍历从i到j后的最大方案数;
int work(int a,int b)
{
if(a>b) return 0;
if(s[a]!=s[b]) return 0;
if(a==b) return 1;
if(f[a][b]!=-1) return f[a][b];
f[a][b]=0;
for(int k=a+2;k<=b;k++)
{
f[a][b]=(f[a][b]+(long long)(work(a+1,k-1)*work(k,b))%M)%M;
}
return f[a][b]%M;
}
signed main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
scanf("%s",s+1);
int len=strlen(s+1);
memset(f,-1,sizeof(f));
cout<<work(1,len);
return 0;
}
以上是关于金字塔 题解的主要内容,如果未能解决你的问题,请参考以下文章