P10956 金字塔

原题链接

虽然我还是看题解才会做的(菜),但我还要说这题挺简单的(菜),还是菜就多练。

因为是对一个字符串操作求树的可能数,所以考虑区间dp,设状态 \(dp[i][j]\) 为区间 \(i\)\(j\) 树的状态数。

在转移的过程中如果 \(s_i=s_j\) 时代表这可能为同一个根节点组成的树,所以才可以开始枚举中间点,当中间点与根节点的颜色相同时,我们就可以将分为两个子树,然后根据乘法原理,将每个区间状态数相乘就是这么划分的方案数,再将所有的状态数相加就得到了大区间的状态数,转移方程为 \(dp[i][j]=(dp[i][j]+dp[i][k]\times dp[k+1][j-1])\),\(k+1\)\(j-1\) 是为了得到两个不同子树乘积。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int mod=1e9;
const int N=1e5;
char s[N];
int n;
ll dp[1005][1005];
int main(){
    ios::sync_with_stdio(false);
	cin>>s+1;
	n=strlen(s+1);
	for(int i=1;i<=n;i++){
		dp[i][i]=1;//初始化,只有1种可能 
	}
	for(int len=2;len<=n;len++){//区间长度 
		for(int i=1;i<=n-len+1;i++){//枚举起点 
			int j=i+len-1;
			if(s[i]==s[j]){//可能合法 
				for(int k=i;k<=j-1;k++){//枚举中间点 
					if(s[k]==s[i]){//得到两个子树 
						dp[i][j]+=(dp[i][k]*dp[k+1][j-1])%mod;
						dp[i][j]%=mod;
					}
				}
			}
		}
	}
	cout<<dp[1][n];
    return 0;
}
posted @ 2024-09-23 09:21  sad_lin  阅读(3)  评论(0编辑  收藏  举报