太精妙,留着以后慢慢消化
https://www.luogu.org/problemnew/show/P1472
法一:
我们一层一层地来推,枚举层数,然后再枚举这一层选择的奶牛的个数(必须是偶数个的,因为我第一层已经处理好了),然后再枚举上一层选择的奶牛的个数(也必须是偶数的,因为我把第二层也特殊处理了,就可以从第三层开始了),但是发现一共要选择n个奶牛,所以不得不再加一维目前总共选择的奶牛的数量,也就是加一层循环来枚举v罢了;现在就是重点!!!假设上一层选择了m只奶牛,这一层选择了j只奶牛,那么m必须满足m>=j/2(因为二叉树的性质),假设上一层的状态为f[i-1][m][v-j],也就是i-1层选择了m只奶牛,前i-1层总共选择了v-j只奶牛的方案总数,这时第i层就是选择了j只奶牛了,第i层的j只奶牛和第i-1层的m只奶牛互相找妈妈,那么可以选择的方案总数就是C(m,j/2)*f[i-1][m][v-j],最后再统计就可以了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll ,ll > P;
#define INF 0xf3f3f3f
const int Max=int(1000+10);
const int mol=9901;
int n,k,dp[100+10][200+10][200+10],w[200+10][200+10];
int main() {
while(~scanf("%d%d",&n,&k)) {
w[0][0]=1;
dp[1][1][1]=1;
dp[2][2][3]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=i;j++)
if(j==0||i==j)
w[i][j]=1;
else
w[i][j]=(w[i-1][j]+w[i-1][j-1])%mol;
for(int i=3;i<=k;i++)
for(int j=2;j<=(n/2+1);j+=2)
for(int x=j+1;x<=n;x++)
for(int y=j/2;y<=n/2+1;y++)
if(y%2==0)
dp[i][j][x]+=w[y][j/2]*dp[i-1][y][x-j]%mol;
ll ans=0;
for(int i=1;i<=n;i++)
ans+=dp[k][i][n];
printf("%lld\n",ans%mol);
}
return 0;
}
法二:
采用记忆化搜索来解决这道题。
我们规定左子树必须正好满足要求,即左子树高度为y-1,而右子树高度小于等于y-1(如果左右子树高度不等,则左右子树可以互换,方案乘以2)然后我们用两个for循环分别枚举左子树的节点数与右子树的高度值(小于等于y-1),利用乘法原理状态转移。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll ,ll > P;
#define INF 0xf3f3f3f
const int Max=int(1000+10);
#define MOD 9901
bool bVis[212][112];
int dp[212][112];
int n, m;
int dfs(int cnt, int height)
{
if (bVis[cnt][height]) return dp[cnt][height];
if (cnt == 1) return height == 1;
bVis[cnt][height] = true;
if (height > (cnt + 1) / 2) return 0;
if (height <= 1) return 0;
int& state(dp[cnt][height]);
for (int i(1); i < cnt; i += 2)
{
for (int j(1); j < height; ++j)
{
state += dfs(i, height - 1) * dfs(cnt - i - 1, j);
state %= MOD;
if (height - 1 != j)//如果高度相等,那么互换反而会导致方案重复(不妨试着模拟一下或者输出参数)
{
state += dfs(i, height - 1) * dfs(cnt - i - 1, j);
state %= MOD;
}
}
}
return state;
}
int main()
{
scanf("%d%d", &n, &m);
printf("%d\n", dfs(n, m));
return 0;
}