[Gym 101334E]Exploring Pyramids(区间dp)

题意:给定一个先序遍历序列,问符合条件的树的种类数

解题关键:枚举分割点进行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;
}
原文地址:https://www.cnblogs.com/elpsycongroo/p/7800589.html