链接
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1194
题解
这题主要难在它的数学建模
可以把最终的
n
e
t
w
o
r
k
network
network看作一棵树,最后一步如果是并联,那么我就把并联起来的这几部分看作子树,根节点代表并联这种连接方式,根节点代表并联,那么下一层肯定代表串联,再往下一层肯定代表并联
叶子节点就代表一条边
这棵子树需要满足叶子数等于
n
n
n,对于任何一棵子树,其子树大小都是递增排列的,每个非叶子节点都至少有两个儿子,这棵树就能代表一个
n
e
t
w
o
r
k
network
network,树的形态个数就是方案数
由于我一开始规定了根节点代表并联,所以答案要乘以
2
2
2才是最终答案(
n
=
1
n=1
n=1特判)
这个东西怎么统计?
假设
f
(
x
)
f(x)
f(x)表示叶子数为
x
x
x时树的形态种类
比较暴力的解法
我只需要对
x
x
x进行整数划分,比如
5
=
1
+
1
+
1
+
2
5=1+1+1+2
5=1+1+1+2,那就是三棵包含
1
1
1个叶子的子树和
1
1
1棵包含
2
2
2个叶子的子树
这种情形对
f
(
5
)
f(5)
f(5)的贡献就是
C
f
(
1
)
−
3
+
1
3
C
f
(
2
)
−
1
+
1
1
C_{f(1)-3+1}^3C_{f(2)-1+1}^1
Cf(1)−3+13Cf(2)−1+11(可重复选择的组合数)
对
[
1
,
30
]
[1,30]
[1,30]中的整数分别做整数划分,然后求,这样时间是允许的,因为
30
30
30的划分不是很大
比较快的解法
f
(
x
)
f(x)
f(x)的意义和上面相同
g
(
i
,
j
)
g(i,j)
g(i,j)表示含有
i
i
i个叶子的子树,子树的每棵子树所包含的叶子数都不超过
j
j
j的方案数
边界条件:
f
(
1
)
=
1
,
g
(
1
,
i
)
=
1
(
i
≥
1
)
f(1)=1,g(1,i)=1(i\geq 1)
f(1)=1,g(1,i)=1(i≥1),
g
(
0
,
i
)
=
1
(
i
≥
0
)
g(0,i)=1(i\geq 0)
g(0,i)=1(i≥0)
转移的时候其实就是考虑当前层叶子数为
j
j
j的子树的数量,
g
(
i
,
j
)
=
∑
p
=
0
⌊
i
j
⌋
(
g
(
i
−
p
j
,
j
−
1
)
C
f
(
j
)
+
p
−
1
p
+
g
(
i
−
p
j
,
+
∞
)
C
f
(
j
)
+
p
−
1
p
[
i
−
p
j
>
1
,
i
−
p
j
<
j
]
)
g(i,j)=\sum_{p=0}^{\lfloor\frac{i}{j}\rfloor}\left(g(i-pj,j-1)C_{f(j)+p-1}^p+g(i-pj,+\infty)C_{f(j)+p-1}^p[i-pj>1,i-pj<j]\right)
g(i,j)=∑p=0⌊ji⌋(g(i−pj,j−1)Cf(j)+p−1p+g(i−pj,+∞)Cf(j)+p−1p[i−pj>1,i−pj<j])
这个
d
p
dp
dp的思想其实是模仿了整数划分的那个
d
p
dp
dp
n
n
n的
m
m
m划分表示为
f
(
n
,
m
)
f(n,m)
f(n,m),那么考虑
m
m
m这个数放不放,得到方程
f
(
n
,
m
)
=
f
(
n
−
m
,
m
)
+
f
(
n
,
m
−
1
)
f(n,m)=f(n-m,m)+f(n,m-1)
f(n,m)=f(n−m,m)+f(n,m−1)
这题因为大小相同的子树必须一起算,因此多了个枚举
p
p
p的举动
收获
这题主要难在数学建模
一些看起来无从下手的题,肯定是进行了某种抽象,它最终指向的肯定还是我们学过的某种模型
所以当问题看起来毫无思路时,不妨把它等价成某种我们熟悉的模型
代码
//数学建模、DP
#include<bits/stdc++.h>
#define maxn 100
#define ll long long
using namespace std;
ll f[maxn], g[maxn][maxn], N;
ll C(ll n, ll m)
{
ll ans=1, i, j=2;
for(i=n;i>=n-m+1;i--)
{
ans*=i;
if(ans%j==0 and j<=m)ans/=j++;
}
return ans;
}
void dp()
{
int i, j, k;
for(i=0;i<maxn;i++)g[0][i]=1; //全放j的情况
for(i=1;i<maxn;i++)g[1][i]=1;
f[1]=1;
for(i=2;i<maxn;i++)
{
for(j=1;j<i;j++)
{
g[i][j]=g[i][j-1];
for(k=1;k*j<=i;k++)
{
g[i][j]+=g[i-k*j][j-1]*C(f[j]+k-1,k);
if(i-k*j<j and i-k*j>1)g[i][j]+=g[i-k*j][maxn-1]*C(f[j]+k-1,k);
}
}
for(j=i;j<maxn;j++)g[i][j]=g[i][j-1];
f[i]=g[i][i-1];
}
}
int main()
{
ll N;
dp();
while(scanf("%lld",&N),N)
{
if(N!=1)printf("%lld\n",f[N]<<1);
else printf("1\n");
}
return 0;
}