51nod AVL树的种类 题解

题目大意:问有多少种有n个节点的AVL树。

经典的dp题,这里讲一下我的思路:

首先想象dp这个数组怎么定义,很显然,我们需要从节点数为1的AVL树推到节点数为n的AVL树(题目也没给其他东西,只能这么推了。。。),那么我们肯定要设dp[i]表示有i个节点的AVL树有多少种形态,但是经过一番深思熟虑,我们发现这个方程貌似好像没有办法转移呀,怎么办呢,我们试着再加一维,那这一维加什么?我们发现,有i个节点的AVL树,可能有多种形态,而这多种形态,它们的高度不是一致的,于是就想到加一维高度,设dp[i][k]表示有i个节点,高度为j的AVL树有多少种形态。那么方程就很好推了。

我们不妨在每次转移的时候,将dp[i][k]中的i当成一个新加进AVL树的节点,那么设每一次这个节点都作为根,那它作为根的话有多少种情况呢?首先要明确AVL树的定义,每个节点的左右子树高度差不超过一,也就是说他们要么高度差为0,要么为1,那么我们分类讨论,假如为0,那么问题就变成了,用i-1个节点,构建i的子树,并且它的子树也要是AVL树,有多少种构建方法。因为有左右子树,所以需要把i-1个节点分成两份,一份构建左子树,一份构建右子树,原本只有i和k两个循环,因为这里要枚举分界点,于是我们再加上一重循环  for(int j=0;j<i;j++)  ,这样就可以得到方程:dp[i][k]+=dp[j][k-1]*dp[i-j-1][k-1],对于每个分界点,左子树的形态数乘右子树的形态数便是总的形态数,因为每一次我们要计算的是以i为根的高度为k的AVL树形态数,所以i的两个子树的高度必定为k-1。类似的,假如它们的高度差为1,那么只需要将其中一个k-1改成k-2即可,dp[i][k]+=dp[j][k-1]*dp[i-j-1][k-2]*2,为什么要乘2呢?因为可能是左子树比右子树高1,也可能是右子树比左子树高1,所以要乘2。

完整方程:dp[i][k]+=dp[j][k-1]*dp[i-j-1][k-1]+dp[j][k-1]*dp[i-j-1][k-2]*2

代码如下:

#include <cstdio>
#include <cstring>
#define mod 1000000007
#define ll long long

ll dp[2010][16];
int n;

int main()
{
	scanf("%d",&n);
	dp[0][0]=dp[1][1]=1;//初始化
	for(int i=2;i<=n;i++)
	{
		for(int j=0;j<i;j++)
		{
			for(int k=2;k<16;k++)
			{
				dp[i][k]+=dp[j][k-1]*dp[i-j-1][k-1]+dp[j][k-1]*dp[i-j-1][k-2]*2;
				dp[i][k]%=mod;
			}
		}
	}
	ll ans=0;
	for(int i=1;i<16;i++)//统计有n个节点的高度不同的AVL树的形态数
	ans+=dp[n][i];
	printf("%lld",ans%mod);
}

 

    原文作者:AVL树
    原文地址: https://blog.csdn.net/a_forever_dream/article/details/81268907
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞