题意:给定一个先序遍历序列,问符合条件的树的种类数
解题关键:枚举分割点进行dp,若符合条件一定为回文序列,可分治做,采用记忆化搜索的方法。
转移方程:$dp[i][j] = sum {dp[i + 1][k - 1]*dp[k][j]} $ 令$dp[i][j]$表示i到j里数量
1、记忆化搜索
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9; ll dp[305][305]; char a[305]; ll dfs(int l,int r){ if(dp[l][r]!=-1)return dp[l][r]; if(l==r) return dp[l][r]=1; if(a[l]!=a[r]) return 0; dp[l][r]=0; for(int i=l+2;i<=r;i+=2){ if(a[i]==a[l]){ dp[l][r]+=dfs(l+1,i-1)*dfs(i,r);//一定注意dp的顺序 dp[l][r]%=mod; } } return dp[l][r]; } int main(){ freopen("exploring.in", "r", stdin); freopen("exploring.out", "w", stdout); while(~scanf("%s", a)){ memset(dp,-1,sizeof dp); int len=strlen(a); printf("%lld ", dfs(0,len-1)%mod); } return 0; }
2、区间dp
#include<bits/stdc++.h> #define mod 1000000000 using namespace std; typedef long long ll; string a; ll dp[305][305],len; int main(){ freopen("exploring.in", "r", stdin); freopen("exploring.out", "w", stdout); while(cin>>a){ memset(dp,0,sizeof dp); len=a.size(); if(len%2==0){ cout<<0<<endl; continue; } for(int i=0;i<len;i++) dp[i][i]=1; for(int i=2;i<len;i+=2){ for(int l=0,r;l+i<len;l++){ r=l+i; if(a[l]==a[r]){ for(int k=l+1;k<=r;k++) if(a[k]==a[l]) dp[l][r]=(dp[l][r]+dp[l+1][k-1]*dp[k][r])%mod; } } } cout<<dp[0][len-1]<<endl; } return 0; }