金字塔(区间dp)
金字塔
https://ac.nowcoder.com/acm/problem/51172
金字塔
利用dfs序的通性,如果[l, r]是一颗完整的子树,一定有str[l] = str[r],
接下来就是考虑如何划分子树了。
考虑对[l, r]的子树进行划分,枚举中间划分值mid,那么因为dfs序一定有str[l] = str[mid] = str[r]。
我们定义dp[l][r],为区间[l][r],也就是以l,为根节点的划分方案数,
当有mid满足条件时,一定有dp[l][r] = dp[l][r] + dp[l + 1][mid - 1] * dp[mid][r]
dp[l + 1][mid - 1]为单独作为子树的方案,
dp[mid][r]这棵树没有加上[l + 1][r - 1]区间的方案树。
然后再特判一个ABA形式的情况,也就是两个节点的树的特殊情况。
之后再进行区间dp转移即可。
/* Author : lifehappy */ #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 310, mod = 1e9; int dp[N][N], n; char str[N]; int main() { // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); scanf("%s", str + 1); n = strlen(str + 1); for(int i = 1; i <= n; i++) { dp[i][i] = 1; } for(int len = 3; len <= n; len++) { for(int l = 1; l + len - 1 <= n; l++) { int r = l + len - 1; if(str[l] != str[r]) continue; dp[l][r] = dp[l + 1][r - 1]; for(int mid = l + 2; mid <= r - 2; mid++) { if(str[l] == str[mid]) { dp[l][r] = (dp[l][r] + 1ll * dp[l + 1][mid - 1] * dp[mid][r] % mod) % mod; } } } } printf("%lld\n", dp[1][n]); return 0; }