https://vjudge.net/problem/UVA-10253
题目
串并联网络有两个端点,一个叫源,一个叫汇,递归定义如下。
(1)一条单独的边是串并联网络。
(2)若G1和G2是串并联网络,把他们的源和汇分别接在一起也能得到串并联网络。
(3)若G1和G2是串并联网络,把G1的汇和G2的源并在一起也能得到串并联网络。
其中规则2说的是并联,规则3说的是串联。
串联的各部分可以改变顺序,并联的各部分可以改变顺序,都看作一种串并联网络。
输入n,输出有多少个n条边的串并联网络。$1\leqslant n\leqslant 30$
题解
每个串并联网络都可以看成一棵树,每次串并联都生成一个节点,串并联的部分是它的儿子,叶子是单独的边
所以问题可以转换为n个叶子能得到多少种树,兄弟顺序无关,每个节点要么没有儿子,要么大于两个儿子
那么可以用背包,划分第一层
dp[i][j]表示有i个叶子,现在划分有j个叶子的子树,有多少种方法
f(x)表示含x个叶子的树有多少种,$f(x)=dp[x][x-1]$,因为每个节点要么没有儿子,要么大于两个儿子
$dp[i][j]=\sum dp[i-j*n][j-1]\times C(f(j)+n-1,n)$
得到树以后还需要指定第一层是串联还是并联
还有一些边界情况
AC代码
#include<cstdio> #include<cstring> #include<algorithm> #define REP(i,a,b) for(int i=(a); i<(b); i++) #define REPE(i,a,b) for(int i=(a); i<=(b); i++) #define PERE(i,a,b) for(int i=(a); i>=(b); i--) using namespace std; typedef long long ll; ll dp[37][37],f[37]; ll C(ll n, ll m) { double ans=1; REP(i,0,m) { ans*=n-i; } REPE(i,1,m) { ans/=i; } return ll(ans+0.5); } int main() { REPE(i,0,30) dp[0][i]=1; REPE(i,1,30) dp[i][0]=0,dp[1][i]=1; f[1]=1; REPE(j,1,30) { REPE(i,2,30) { dp[i][j]=0; for(int n=0; n*j<=i; n++) { dp[i][j]+=dp[i-j*n][j-1]*C(f[j]+n-1,n); } } f[j+1]=dp[j+1][j]; } int N; while(~scanf("%d", &N) && N) { printf("%lld\n", N==1?1:f[N]*2); } }
原文地址:https://www.cnblogs.com/sahdsg/p/12643693.html
时间: 2024-11-09 04:03:55