hdu :4359(记忆化搜索DP)

10 篇文章 0 订阅

左子树的和小于右子树的和 由于都是2的幂 所以只要且必须右子树上有剩下 数中最大的那个就可以了(当然左/右为空的特殊)

查询时注意 根节点的选取是任意的 所以有假如说有n个节点 那根节点就有n种选法 还有根节点选出后 如果左右都有节点

那么剩余最大节点一定在右面

所以情况

1 左/右为空

2 左右深度同时为D-1

3 只有左/右深度为D-1

要注意时刻取模

这题会用到组合中的从几个点中选几个点 的情况数量 由于360太大 所以轻松超long long

#include<iostream> 
#include<cstdio> 
#include<cstring> 
#include<algorithm> 
#include<map> 
#include<stack> 
#include<cmath> 
#define LL long long 
  
using namespace std; 
  
const LL MOD=1000000007; 
const int N=365; 
LL ans[N][N];//节点数为i 深度为j 的情况数 
LL c[N][N];//从i 个节点中选取 j 个节点的情况数 
LL te[N][N];// 有i 个节点 组合成的 深度从1 到 j 的情况数 只是怕有重复的情况 避免浪费时间 
LL dp(int ,int ); 
inline LL Ftemp(int k1,int k2) 
{ 
    if(te[k1][k2]!=-1) 
    return te[k1][k2]; 
    te[k1][k2]=0; 
    for(int l=1;l<=k1&&l<=k2;++l) 
    { 
        te[k1][k2]=(te[k1][k2]+dp(k1,l))%MOD;//枚举 深度 取模 
    } 
    return te[k1][k2]; 
} 
LL dp(int i,int d) 
{ 
    if(ans[i][d]!=-1) 
    return ans[i][d]; 
    if(i<d)//特殊情况 深度不能超过节点数量 
    { 
        ans[i][d]=0; 
        return ans[i][d]; 
    } 
    if(i==1||d==1)//边界 
    { 
        if(i==1&&d==1)//若都为1 则为答案 1 
        ans[i][d]=1; 
        else
        ans[i][d]=0;//否则为0 
        return ans[i][d]; 
    } 
    ans[i][d]=(dp(i-1,d-1)*2)%MOD;//左/右为空的情况 
    for(int j=d-1;j<i-1&&i-j-1>=d-1;++j) 
    { 
        ans[i][d]=(ans[i][d]+(((dp(j,d-1)*dp(i-j-1,d-1))%MOD)*c[i-2][j-1])%MOD)%MOD;//两边深度都为D-1 的情况 
    } 
    for(int j=d-1;j<i-1&&i-1-j>0;++j) 
    { 
        LL temp=Ftemp(i-1-j,d-2);//有 i-1-j 个节点  深度最大为d-2 的情况数 
        ans[i][d]=(ans[i][d]+(((dp(j,d-1)*temp)%MOD)*c[i-2][j-1])%MOD)%MOD;//右边深度为D-1 左边深度小于D-1 的情况 
        ans[i][d]=(ans[i][d]+(((dp(j,d-1)*temp)%MOD)*c[i-2][j])%MOD)%MOD;//左边深度为D-1 右边深度小于D-1 的情况 它们只在组合数上不同 
    } 
    ans[i][d]=(ans[i][d]*i)%MOD;//根节点的选取有i 种情况 
    return ans[i][d]; 
} 
void begin() 
{ 
    for(int i=0;i<N;++i) 
    c[i][0]=1; 
    for(int i=1;i<N;++i) 
    { 
        for(int j=1;j<=i;++j) 
        { 
            if(i==j) 
            c[i][j]=1; 
            else
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;//组合递推法  一定要牢记呀 吸取教训 
        } 
    } 
} 
int main() 
{ 
  
    //freopen("data.txt","r",stdin); 
    int T; 
    begin(); 
    memset(ans,-1,sizeof(ans)); 
    memset(te,-1,sizeof(te)); 
    scanf("%d",&T); 
    for(int cas=1;cas<=T;++cas) 
    { 
        int n,d; 
        scanf("%d %d",&n,&d); 
        printf("Case #%d: ",cas); 
        cout<<dp(n,d)<<endl; 
    } 
    return 0; 
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值